Return-Path: Received: from fort-point-station.mit.edu by po10.mit.edu (8.9.2/4.7) id LAA19068; Tue, 19 Feb 2002 11:56:25 -0500 (EST) Received: from hermes.sun.com (hermes.sun.com [64.124.140.169]) by fort-point-station.mit.edu (8.9.2/8.9.2) with SMTP id LAA27369 for ; Tue, 19 Feb 2002 11:56:24 -0500 (EST) Date: Tue, 19 Feb 2002 16:56:24 GMT+00:00 From: "JDC Tech Tips" To: alexp@mit.edu Message-Id: <1009244896006989@hermes.sun.com> Subject: JDC Tech Tips, February 19, 2002 (Java Media Framework, JSObject) Precedence: junk Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit X-Mailer: Beyond Email J D C T E C H T I P S TIPS, TECHNIQUES, AND SAMPLE CODE WELCOME to the Java Developer Connection(sm) (JDC) Tech Tips, February 19, 2002. This issue covers: * Playing Audio and Video With the Java(tm) Media Framework * Using the JSObject Class in Applets These tips were developed using Java 2 SDK, Standard Edition, v 1.3 and Java Media Framework 2.1.1. This issue of the JDC Tech Tips is written by John Zukowski, president of JZ Ventures, Inc. (http://www.jzventures.com). You can view this issue of the Tech Tips on the Web at http://java.sun.com/jdc/JDCTechTips/2002/tt0219.html - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PLAYING AUDIO AND VIDEO WITH THE JAVA MEDIA FRAMEWORK You can use the Core Java 2 Platform libraries to display still images in GIF, JPEG, and PNG format. The libraries also provide limited support for displaying animation through the GIF89A image format. You can also play WAV, AU, MIDI, and AIFF-formatted audio files. This support might be sufficient for your programs, but if you need to work with other rich media formats, such as AVI files for video or MP3 files for audio, you need the Java Media Framework (JMF) API. The JMF API supports the playing, streaming, and capturing of audio and video. It provides a series of encoders and decoders to support different formats and offers a pluggable architecture for you to add support for additional formats. The latest version of the Java Media Framework software (JMF 2.1.1 a) is available for download from http://java.sun.com/products/java-media/jmf/. The JMF download comes in two flavors: a platform-specific version and a cross-platform version. Sun provides platform-specific versions for Solaris and Windows. (A Linux version is also available from Blackdown.) The Sun platform-specific versions include audio support through native libraries for the Java Sound API, the cross-platform version does not. After downloading the installation, you need to configure your machine to use the JMF libraries. With the platform-specific library versions, you need to add the jmf.jar and sound.jar files to your class path, and the JMF lib directory to your path. For the cross-platform version, you need to add a reference to the jmf.jar file into your class path. The Linux version comes in three flavors and supports both audio and video. For additional information and installation instructions see http://www.blackdown.org/java-linux/jdk1.2-status/jmf-status.html Playing multimedia files through the JMF libraries is simple. The key classes are Manager and Player. The Manager has a series of createPlayer() methods that each return a Player. After you create a Player, you tell the Player to start playing. For basic audio playing, all you need to do is: Player player = Manager.createPlayer(resource); player.play(); The resource you pass into the createPlayer method can be one of three things: DataSource, MediaLocator, or URL. These represent different ways of specifying media: as a protocol handler (DataSource), through its content (MediaLocator), or by it location (URL). In most cases, working with the URL resources is simplest. For example, if you want to play a file that is on your local hard drive, you simply: 1. Get the URL for the file through the toURL method of the File class). 2. Pass the URL as the resource to createPlayer. 3. Play the file using the play method: URL url = file.toURL(); Player player = Manager.createPlayer(url); player.play(); This process is not sufficient to play video. Calling play() for a video file is like watching your favorite video with the television set turned off. You might hear the audio but there's no picture. In order to get a picture you need to do a little extra work, such as register a ControllerListener. The Player is a type of Controller, and controllers let you register a ControllerListener. The ControllerListener contains a single method public void controllerUpdate(ControllerEvent event). You use the method to find out when various events happen with the media, such as when the end of a video file is reached, when an audio file has been loaded, or when the playing of media data has started and stopped. To respond to these events use the ControllerAdapter class. ControllerAdapter offers more than thirty different methods to respond to specific types of controller events. Each of these methods dispatches to a stub-method. Typically, you subclass the ControllerAdapter to override the specific method or methods of interest with your own event-handling logic. If you don't use this approach, you need to check for the correct type of event using instanceof in the controllerUpdate method. An event subtype that is particularly important is RealizeCompleteEvent. When this event happens, the ControllerAdapter delegates the handling to the realizeComplete method. Overriding this method permits you to get the visual component for the video player and the control panel component for audio and video playing. The control panel is where you can control audio volume, and start or stop video. Here is how you can get the different components and add them to your display. Component vc = player.getVisualComponent(); if (vc != null) { contentPane.add(vc, BorderLayout.CENTER); } Component cpc = player.getControlPanelComponent(); if (cpc != null) { contentPane.add(cpc, BorderLayout.SOUTH); } The following example puts together all the features discussed here. The program provides a button that brings up a JFileChooser to select what file you want to play. The program then acquires a player for the file, and starts playing the file. This triggers the acquisition of the visual components being added to the screen. All the JMF-related code is in the load method of the program. Beyond that, the remainder of the code just manages the GUI. You might wonder why the program stops the player before it starts another one. When you issue a Manager.createPlayer(url) call to create a new Player object, it is necessary to stop the prior one before starting the new one. If you don't stop the old player, you might hear an MP3 file playing while watching a video with its own sound track. If the video plays first, you need to remove the visual component for the video player, or else you see the "old" video clip, while listening to the new audio file. import javax.swing.*; import javax.media.*; import java.awt.*; import java.awt.event.*; import java.net.*; import java.io.*; public class PlayVideo extends JFrame { Player player; Component center; Component south; public PlayVideo() { setDefaultCloseOperation(EXIT_ON_CLOSE); JButton button = new JButton("Select File"); ActionListener listener = new ActionListener() { public void actionPerformed( ActionEvent event) { JFileChooser chooser = new JFileChooser("."); int status = chooser.showOpenDialog(PlayVideo.this); if (status == JFileChooser.APPROVE_OPTION) { File file = chooser.getSelectedFile(); try { load(file); } catch (Exception e) { System.err.println("Try again: " + e); } } } }; button.addActionListener(listener); getContentPane().add(button, BorderLayout.NORTH); pack(); show(); } public void load(final File file) throws Exception { URL url = file.toURL(); final Container contentPane = getContentPane(); if (player != null) { player.stop(); } player = Manager.createPlayer(url); ControllerListener listener = new ControllerAdapter() { public void realizeComplete( RealizeCompleteEvent event) { Component vc = player.getVisualComponent(); if (vc != null) { contentPane.add(vc, BorderLayout.CENTER); center = vc; } else { if (center != null) { contentPane.remove(center); contentPane.validate(); } } Component cpc = player.getControlPanelComponent(); if (cpc != null) { contentPane.add(cpc, BorderLayout.SOUTH); south = cpc; } else { if (south != null) { contentPane.remove(south); contentPane.validate(); } } pack(); setTitle(file.getName()); } }; player.addControllerListener(listener); player.start(); } public static void main(String args[]) { PlayVideo pv = new PlayVideo(); } } For more information about using JMF, see the Java Media Framework API Guide at http://java.sun.com/products/java-media/jmf/2.1.1/guide/JMFTOC.html - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - USING THE JSOBJECT CLASS IN APPLETS Applets are Java technology programs that run in the browser. Under most circumstances, an applet does its processing within the realm of the Java Runtime Environment (JRE). There are times however when it is necessary to jump out of the applet's secure runtime environment (the "sandbox") and communicate with the browser. The communication is done through the LiveConnect facility, and permits applets to work with JavaScript. The bulk of this communication is performed through the netscape.javascript.JSObject class. Originally provided as part of the Netscape browser environment, Microsoft eventually added this package to the runtime that ships with Internet Explorer. In addition, the package is now a standard part of the Java Runtime Environment installed with the Java Plug-in. That means that applets can use the package and expect to find the package when they execute. It isn't necessary to include the classes with a deliverable program. In order to develop applets that take advantage of this package, you need to add the necessary classes to your class path. The Java runtime environment already puts these classes in your runtime class path, by default. However the development environment doesn't. So you need to make available to your development environment the jaws.jar file (Windows) or javaplugin.jar file (Solaris) that ships with the Java Runtime. Assuming that you use the Java 2 Standard Edition (J2SE) version 1.3 Software Development Kit (SDK), you can place the jaws.jar file (Windows) in the jre\lib\ext directory or javaplugin.jar file (Solaris) in the jre/lib/ext directory under the installation directory for your SDK. Once copied, the appropriate classes (and others) will be available to your program. As previously mentioned, the key class for applet-to-JavaScript communications is the JSObject class. This class provides, among other things, the means to call JavaScript methods and evaluate raw JavaScript syntax. To demonstrate these capabilities, let's create an applet that prompts for raw HTML in its TextArea. Then let's open up a new browser window to display the HTML. Because the raw HTML is in the TextArea and not in a file, you can't just call the showDocument() method to open up a new URL. The first thing you need to do to communicate with JavaScript is get a JSObject instance. The class has no constructor. Instead, you call the getWindow method of the class, passing in a parameter of the applet instance: JSObject topWindow = JSObject.getWindow(this); Calling methods on the JSObject object is done using the call method. This method works by passing in the text name of the method to invoke, and an Object array of the arguments. public Object call(String name, Object args[]) To pass in arguments, you create the array, and then fill it, based upon how many arguments are necessary: Object args[] = new args[x] args[0] = ...; args[1] = ...; ... args[x] = ...; For instance, to open a window you need to call the open method of the window object. Because getWindow returns the window associated with the applet, you already have the window object. The open method accepts three arguments: * The URL of what to open * The name of the window * Any attributes as a comma-separated list The attributes describe how the window will look. Some of the more standard ones are: * width (number) * height (number) * location (boolean) * menubar (boolean) * scrollbars (boolean) * status (boolean) * toolbar (boolean) The boolean values can be yes, no, 1, or 0. For instance, if you want a 300x300 window with no location, menubar, status bar, or toolbar, the attribute string would look like this: "width=300,height=300,location=0,menubar=0,status=0,toolbar=0" Using that attribute string, you can open an unnamed window (with no content) with a three element array where only the third element is set: Object args[] = {"","","width=..."}; Then, pass those arguments and the method name on to the call method of the window received from the prior getWindow call. topWindow.call("open", args); To actually fill the window with the contents from the TextArea, you must call the write method on the named "document" member within the window. Getting the document member involves calling the getMember method and passing it the name of the member you want, in this case, document: JSObject document = ( JSObject)popupWindow.getMember("document"); You then need to call the write method of document to change the content of the window. Here, you simply construct another argument array, this time with only one element for the text: String htmlText = ...; args = new Object[] {htmlText}; Then, call the write method: document.call("write", args); That's essentially all there is to communicating with the JSObject object. Another thing you might want to do with JSObject is call the eval method. Given a string, the eval method treats the string as a JavaScript expression and evaluates it. Here is an applet that puts together all the pieces that were covered in this tip: import java.applet.*; import java.awt.*; import java.awt.event.*; import netscape.javascript.*; public class JSPopup extends Applet { public void init() { final TextArea ta = new TextArea(); Button button = new Button("Launch"); ActionListener listener = new ActionListener() { public void actionPerformed(ActionEvent e) { String htmlText = ta.getText(); JSObject topWindow = JSObject.getWindow(JSPopup.this); Object args[] = new Object[3]; args[2] = "width=300,height=300," + "location=0,menubar=0,status=0,toolbar=0"; JSObject popupWindow = (JSObject)topWindow.call("open", args); JSObject document = (JSObject) popupWindow.getMember("document"); args = new Object[] {htmlText}; document.call("write", args); } }; button.addActionListener(listener); setLayout(new BorderLayout()); add(ta, BorderLayout.CENTER); add(button, BorderLayout.SOUTH); } } When using applet-to-JavaScript communications, you must set the MAYSCRIPT parameter. This tells the security environment to allow the two systems to communicate. Compile the applet and then run it in your browser. Then enter some HMTL content in the text area:

Hello, World

Then click the Launch button in the applet. You should see a new window displayed. If you don't see a new window, try using a different browser (a number of bugs have been reported regarding Java-JavaScript communications with 1.3.1 Plugin and Netscape). You could also use this technique to display the response from applet-to-servlet communications where the applet has to manually send a POST request to the servlet. Even without Java Plugin installed, you can still use the netscape.javascript.JSObject class with the native virtual machine in Internet Explorer 4.x - 6.x and Netscape Communicator 4.7x. The JavaScript communications support in a native virtual machine differs from that provided with the Java Plug-in. The basics described in this tip are the same. However, the native virtual machines rely on an older version of the package. You can find documentation for the older package at http://home.netscape.com/eng/mozilla/3.0/handbook/plugins/doc/ Package-netscape.javascript.html . . . . . . . . . . . . . . . . . . . . . . . IMPORTANT: Please read our Terms of Use, Privacy, and Licensing policies: http://www.sun.com/share/text/termsofuse.html http://www.sun.com/privacy/ http://developer.java.sun.com/berkeley_license.html * FEEDBACK Comments? Send your feedback on the JDC Tech Tips to: jdc-webmaster@sun.com * SUBSCRIBE/UNSUBSCRIBE - To subscribe, go to the subscriptions page, (http://developer.java.sun.com/subscription/), choose the newsletters you want to subscribe to and click "Update". - To unsubscribe, go to the subscriptions page, (http://developer.java.sun.com/subscription/), uncheck the appropriate checkbox, and click "Update". - To use our one-click unsubscribe facility, see the link at the end of this email: - ARCHIVES You'll find the JDC Tech Tips archives at: http://java.sun.com/jdc/TechTips/index.html - COPYRIGHT Copyright 2002 Sun Microsystems, Inc. All rights reserved. 901 San Antonio Road, Palo Alto, California 94303 USA. This document is protected by copyright. For more information, see: http://java.sun.com/jdc/copyright.html JDC Tech Tips February 19, 2002 Sun, Sun Microsystems, Java, and Java Developer Connection, are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. To use our one-click unsubscribe facility, select the following URL: http://bulkmail.sun.com/unsubscribe?1009244896006989