/* * BufferedPlayback.java * * Copyright � 1998-2011 Research In Motion Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Note: For the sake of simplicity, this sample application may not leverage * resource bundles and resource strings. However, it is STRONGLY recommended * that application developers make use of the localization features available * within the BlackBerry development platform to ensure a seamless application * experience across a variety of languages and geographies. For more information * on localizing your application, please refer to the BlackBerry Java Development * Environment Development Guide associated with this release. */ package com.rim.samples.device.bufferedplaybackdemo; import javax.microedition.media.Manager; import javax.microedition.media.Player; import net.rim.device.api.ui.Field; import net.rim.device.api.ui.FieldChangeListener; import net.rim.device.api.ui.UiApplication; import net.rim.device.api.ui.component.BasicEditField; import net.rim.device.api.ui.component.ButtonField; import net.rim.device.api.ui.component.Dialog; import net.rim.device.api.ui.container.HorizontalFieldManager; import net.rim.device.api.ui.container.MainScreen; /** * Provides a GUI interface for buffered media playback from a remotely streamed * source. */ public final class BufferedPlayback extends UiApplication { /** * Entry point for application * * @param args * Command line arguments (not used) */ public static void main(final String[] args) { // Create a new instance of the application and make the currently // running thread the application's event dispatch thread. final BufferedPlayback app = new BufferedPlayback(); app.enterEventDispatcher(); } /** * Creates the main screen and pushes it onto the display stack */ public BufferedPlayback() { final BufferedPlaybackScreen screen = new BufferedPlaybackScreen(); pushScreen(screen); } /** * Presents a dialog to the user with a given message * * @param message * The text to display */ public static void errorDialog(final String message) { UiApplication.getUiApplication().invokeLater(new Runnable() { public void run() { Dialog.alert(message); } }); } /** * The main screen of the application */ private static final class BufferedPlaybackScreen extends MainScreen implements FieldChangeListener { /** A field used to enter the URL of the remote media file */ private final BasicEditField _urlField; /** A field used to enter the MIME type of the remote media file */ private final BasicEditField _mimeField; /** A field used to display the number of bytes that have been loaded */ private final BasicEditField _loadStatusField; /** A field used to display the current status of the media player */ private final BasicEditField _playStatusField; /** * A field which contains the minimum number of bytes that must be * buffered before the media file will begin playing. */ private final BasicEditField _startBufferField; /** * A field which contains the minimum forward byte buffer which must be * maintained in order for the video to keep playing. If the forward * buffer falls below this number, the playback will pause until the * buffer increases. */ private final BasicEditField _pauseBytesField; /** * A field which contains the minimum forward byte buffer required to * resume playback after a pause. */ private final BasicEditField _resumeBytesField; /** A field which contains the maximum byte size of a single read */ private final BasicEditField _readLimitField; /** A button which starts the HTTP request and media playback */ private final ButtonField _startPlayingButton; /** A button which stops the HTTP request and media playback */ private final ButtonField _stopPlayingButton; /** A button which erases current request and playback progress */ private final ButtonField _resetField; /** A stream for the resource we are retrieving */ private LimitedRateStreamingSource _source; /** A player for the media stream. */ private Player _player; /** A thread which creates and starts the Player */ private PlayerThread _playerThread; /** * Creates a new BufferedPlaybackScreen object */ private BufferedPlaybackScreen() { // Set the title of the window setTitle("Buffered Playback Demo"); // Create and add the field for the URL to be retrieved _urlField = new BasicEditField("Media URL: ", ""); add(_urlField); // Create and add the field for the MIME type of the remote file _mimeField = new BasicEditField("Mime: ", "audio/mpeg", 10, Field.NON_FOCUSABLE); add(_mimeField); // Create the START, STOP and RESET buttons _startPlayingButton = new ButtonField("Play", ButtonField.CONSUME_CLICK); _stopPlayingButton = new ButtonField("Stop", ButtonField.CONSUME_CLICK); _resetField = new ButtonField("Reset", ButtonField.CONSUME_CLICK); _startPlayingButton.setChangeListener(this); _stopPlayingButton.setChangeListener(this); _resetField.setChangeListener(this); // Add the player control buttons to the screen final HorizontalFieldManager buttonlist = new HorizontalFieldManager(); buttonlist.add(_startPlayingButton); buttonlist.add(_stopPlayingButton); buttonlist.add(_resetField); add(buttonlist); // Create and add the field with the load progress _loadStatusField = new BasicEditField("Load: ", "0 Bytes", 10, Field.NON_FOCUSABLE); add(_loadStatusField); // Create and add the field with the player status _playStatusField = new BasicEditField("Play: ", "Stopped", 10, Field.NON_FOCUSABLE); add(_playStatusField); // Create and add the field with the starting buffer _startBufferField = new BasicEditField("Starting Buffer: ", "200000", 10, BasicEditField.FILTER_INTEGER | Field.NON_FOCUSABLE); add(_startBufferField); // Create and add the field with the minimum pause buffer _pauseBytesField = new BasicEditField("Pause At: ", "64000", 10, BasicEditField.FILTER_INTEGER | Field.NON_FOCUSABLE); add(_pauseBytesField); // Create and add the field with the minimum resume buffer _resumeBytesField = new BasicEditField("Resume At: ", "128000", 10, BasicEditField.FILTER_INTEGER | Field.NON_FOCUSABLE); add(_resumeBytesField); // Create and add the field with the read limit _readLimitField = new BasicEditField("Read Limit: ", "32000", 10, BasicEditField.FILTER_INTEGER | Field.NON_FOCUSABLE); add(_readLimitField); } /** * A common listener for all three player controls * * @param field * The field that changed * @param context * Information specifying the origin of the change */ public void fieldChanged(final Field field, final int context) { try { // If the START button was pressed, begin playback if (field == _startPlayingButton) { // The player does not exist, we must initialize it if (_player == null) { // Create a stream using the remote file. _source = new LimitedRateStreamingSource(_urlField .getText()); // Set the attributes of the stream using the // information from the GUI fields. _source.setContentType(_mimeField.getText()); _source.setStartBuffer(Integer .parseInt(_startBufferField.getText())); _source.setReadLimit(Integer.parseInt(_readLimitField .getText())); _source.setResumeBytes(Integer .parseInt(_resumeBytesField.getText())); _source.setPauseBytes(Integer.parseInt(_pauseBytesField .getText())); _source.setLoadStatus(_loadStatusField); _source.setPlayStatus(_playStatusField); // Acquire the UI lock UiApplication.getUiApplication().invokeLater( new Runnable() { public void run() { // Update the player status _playStatusField.setText("Started"); } }); // Create and run the player's thread _playerThread = new PlayerThread(); _playerThread.start(); } // The player already exists, simply resume it else { _player.start(); } } // If the STOP button was pressed: else if (field == _stopPlayingButton) { // Acquire the UI lock UiApplication.getUiApplication().invokeLater( new Runnable() { public void run() { // Update the status fields _playStatusField.setText("Stopped"); } }); if (_player != null) { // Stop the playe. _player.stop(); } } // If the RESET button was pressed: else if (field == _resetField) { // Acquire the UI lock UiApplication.getUiApplication().invokeLater( new Runnable() { public void run() { // Update the status fields _loadStatusField.setText("0 Bytes"); _playStatusField.setText("Stopped"); } }); // Destroy the Player and streams destroy(); } } catch (final Exception e) { errorDialog(e.toString()); } } /** * Called when the application exits, ensures that all attributes are * destroyed correctly. */ public void close() { try { // Destroy the Player and streams destroy(); } catch (final Exception e) { errorDialog("destroy() threw " + e.toString()); } finally { super.close(); } } /** * Prevent the save dialog from being displayed * * @see net.rim.device.api.ui.container.MainScreen#onSavePrompt() */ protected boolean onSavePrompt() { return true; } /** * Destroy the Player and streams * * @throws Exception * Thrown if the media player could not be closed or if the * connection stream could not be closed */ private void destroy() throws Exception { // Destroy the player. if (_player != null) { _player.stop(); _player = null; } // Destroy the stream. if (_source != null) { _source.stop(); _source.disconnect(); _source = null; } } /** * A thread for the media player */ private class PlayerThread extends Thread { /** * Create and start the player * * @see java.lang.Runnable#run() */ public void run() { try { _player = Manager.createPlayer(_source); _player.start(); } catch (final Exception e) { // Acquire the UI lock UiApplication.getUiApplication().invokeLater( new Runnable() { public void run() { _playStatusField.setText("Stopped"); Dialog.alert("Error: " + e.getMessage()); } }); } } } } }