By Aviv Eyal
The Internet is the world's richest multimedia file source. Free media playback software, such as RealNetworks' RealPlayer or Microsoft's Windows Media Player, allow users to tap into this source and stream the media over the Internet to their PCs. Using Java and Visual J++ 6.0 (VJ6), it's easy to take this technology to the next level. This article shows how to design, implement, and deploy your own custom media playback Web application using Internet Java APIs, the WFC, COM, and VJ6's RAD features. It also demonstrates how to make practical use of several helpful software architecture and modeling techniques, such as system componentization, components collaboration, state machines, and application events.
Motivation
One of the beauties of present World Wide Web and multimedia technologies is that the tech-savvy hobbyist can build a personal Web application that is the digital equivalent of a home-built ham radio.
Faithful to hobbyists' historical roots in the PC revolution, and to my hobby of buying and listening to hip-hop records, I've set off to build a jukebox that will play the records available at my favorite online hip-hop store, Sandbox Automatic (http://www.sandboxautomatic.com).
At this store, customers can click on any available record in stock to hear a streaming audio sample. This is a great example of Internet multimedia. However, I find this process a little too manual and time consuming. Wouldn't it be neat to build an Internet jukebox that would automatically play all currently available media samples one after another? A record's details should be displayed by the jukebox so the listener can take note of tracks he or she would like to purchase.
Here's a primal and rather simple use case scenario for the Internet jukebox: A user launches the Internet jukebox through his or her Web browser. Once activated, the jukebox starts playing a default play set - samples of all new 12" records available for listening at the Sandbox Automatic store. At any time, the user can choose a different play set. This will initiate a consecutive playback of the set's media. The user is able to stop media playback. Doing so will start the playback of the next available media in the play set. The playback stops when the user quits the jukebox.
In this article, I'll detail techniques for the jukebox's design, implementation, packaging, and deployment, in hopes that others can construct similar radio- and TV-like playback devices for the Internet.
Jukebox System Architecture
The system's high-level design consists of the following components:
§ application logic: implements the application's states and coordinates collaborations of the other components.
§ presentation layer: displays playback information and manages the jukebox GUI.
§ media playback logic: handles Internet multimedia file playback.
§ media search engine: extracts media files and media sources URLs from Internet media sources URLs.
Components Collaborations
The jukebox application logic provides the media search engine with media sources URLs. Media sources are WWW HTML files that contain URL links to actual media files (in our case audio-only .ram files) or other media sources. The media search engine searches the Internet for media files URLs and groups them into play sets. Play set files are sequentially passed back to the application logic, which handles them to the media playback logic for playback. The playback logic streams the media data from the Internet and notifies the application of playback state events.
For example, when a media finishes playing, the application is notified so it can obtain the next play set media from the media search engine and instructs the playback logic to play it. The presentation layer manages the jukebox user interface (see Figure 1).
Figure 1: The Internet jukebox
presentation layer.
The UI consists of playback-specific widgets, such as Play, Stop, and Volume Control, as well as application-specific widgets, such as the play set selection buttons. It funnels the appropriate UI events from the GUI to the application logic. The playback logic handles playback information to the presentation layer for visual display. Figure 2 is a graphical model of these collaborations.
Figure 2: Jukebox components and
their collaborations.
Application Logic States
Figure 3 shows the state machine of our jukebox application logic. Upon jukebox startup (event 1), a Web search for media files is initiated for the default play set. With this event, the machine moves to idle mode (state 0). The applications remains in this state until the media search engine notifies it about the first found media file (event 2). This URL is passed to the playback logic for playback. With this event, the application moves to play mode (state 1). It remains in this state, fetching and initiating playback of new polled found media in response to finished media playing events that are generated by the playback component (event 3). When the user chooses to hear a different play set (event 4), the application sets the appropriate media source on the media search engine and moves to state 0.
Figure 3: State machine for the
Jukebox application logic.
Jukebox Design and Implementation
The application logic and presentation layer are implemented as a Java WFC UserControl component. This implementation decision is by far the "RADest" option for deploying an Internet Explorer-based Web application with VJ6 because it allows you to visually design the presentation layer using the Form Designer, and to deploy the jukebox as an ActiveX control that is embedded in a custom HTML Web page. The Web page provides the execution context and window for the jukebox. Using the Form Designer has other RAD-like advantages. It allows you to visually set the widgets properties and wire up their UI events to the appropriate application logic event handler. As we'll see later in this article, the Form Designer is also helpful in implementing our jukebox state machine events and event handlers.
Implementing the media playback layer is an ideal example of black box reuse; do we need to re-invent the wheel and write streaming media playback logic when we can take advantage of ready-to-use and free multimedia playback COM components that are already installed on millions of PCs, or that are readily available for downloading from the Web?
Both RealPlayer's and Windows Media Player's minimal installations contain media playback COM components that are respectively packaged in RMOC3260.dll and MSDXM.ocx.
The obvious disadvantage of this approach is that our jukebox users must have the playback components already installed on their PCs. However, its advantages clearly outweigh this; our audio playback will be based upon reliable code already used by millions of Internet users. In addition, our users will appreciate the better performance of this Web application; its code download time is greatly reduced, as it doesn't include the playback component bits. Both components also provide media streaming, which enhances our user's experience by eliminating the need for lengthy silent downloads.
Both of the playback COM components include a customizable playback presentation layer. This layer includes the playback control widgets, playback state, and track information. Reusing one of these components will save you the grunt work of designing and implementing these required jukebox features in our own presentation layer.
We can use the VJ6 Form Designer and Toolbox features to add one of these playback components to our jukebox's presentation layer, customize it to our needs using the Properties window, and hook up its relevant playback events to our application logic. This article's source code (available for download; see end of article for details) uses the RealPlayer control. However, you can easily modify the jukebox to use the Windows Media Player by following the instructions and tips in the sidebar, "Jukeboxing with the Windows Media Player."
Figure 4 shows the jukebox's presentation layer using the Windows Media Player. By using one of these components in an interchangeable manner, we also gain an important first-hand technical lesson that will enable us to cut through some hype and compare these competing multimedia technologies in a real-world test case on an objective, feature-per-feature basis.
Figure 4: Presentation layer using
the Windows Media Player.
Media Search Engine Design
The media search engine component is implemented in a Java package that can be conveniently reused in other Internet programming Java projects by compiling and packaging it as a COM component using VJ6, or as a zipped Java class files library. Describing the packaging process is outside the scope of this article, and we'll simply use its .java source files.
The mediaFinder package includes two public interfaces: IInetMediaProvider, which defines an Internet media provider type; and IMediaObserver, which allows implementing clients to observe IInetMediaProvider events (see Figure 5). These public interfaces are designed with reuse in mind; they allow different clients to perform a wide range of Internet media searches. Our jukebox uses a mediaFinder to locate audio .ram files, however, a mediaFinder may also be used to locate and play other types of media, e.g. all Star Wars QuickTime movie clips from the ILM Web site.
package mediaFinder;
public interface
IInetMediaProvider extends Runnable
{
public
void initProvider( String sMediaType,
int nMaxFiles,
boolean fStayInHost,
boolean fSpiderMode,
boolean fRandomizeMedia );
public
String getNextAvailableMedia();
public void addObserver(
IMediaObserver iObserver );
public
void removeObserver( IMediaObserver iObserver
);
public
void addMediaSource( String urlSource );
public
void removeAllMediaSources();
}
package mediaFinder;
public interface
IMediaObserver
{
public
boolean onNewMediaAvailable( String mediaURL );
}
Figure 5: The media search engine's public interfaces.
IInetMediaProvider is initialized to retrieve up to a maximum number of Internet media files of a specific media type (e.g. .mov, .ram, and .asp). Media clients must implement the IMediaObserver interface and add themselves as observers to a media provider, using IInetMediaProvider.addObserver. All observers are notified about new media files through the onNewMediaAvailable event. Clients must provide the IInetMediaProvider with media sources (Internet HTML files that may contain hyperlink references to media files) using the addMediaSource method.
A media provider searches for media files URLs in all its media sources URLs. It works both in synchronous and asynchronous modes. This approach provides great flexibility, yet doesn't add a lot of interface complexity. Any registered observer is notified as soon as a new media is found through the onNewMediaAvailable event. This media can be fetched from the method's argument. An IMediaObserver returning true on this event will cause the provider to discard the media so it won't be available for all the media observers registered on the media provider.
getNextAvailableMedia can be used to asynchronously poll a media provider to fetch the next available media file. This method always removes the media from the provider. Notice that IInetMediaProvider extends Runnable to ensure its implementations, and clients are designed to perform the Internet search in a client-provided thread.
This strategy allows clients to initiate an asynchronous Internet search, continue to execute their code, and obtain search results, as they are available on the search thread. The actual Internet search must be implemented by an IInetMediaProvider in a public synchronized void run method. The code presented in Figure 6 demonstrates how a client may use a media provider implementation to perform a media Web search.
// New CInetMediaProvider or any other
available implementation.
IInetMediaProvider myProvider = new
CInetMediaProvider;
myProvider .initProvider( ".ram", 600, true,
true, true );
MyProvider.addObserver( this
); // This
implements ImediaObserver.
myProvider .AddMediaSource("http://www..sandboxautomatic.com");.
Thread myProviderThread = new
Thread( myProvider);
MyProviderThread.start(); // Execute the search.
// Execute other code here.
// Provider will fire
this.onNewMediaAvailable() on the
// myProviderThread thread for each
fetched media. To poll the
// provider, this can call
getNextAvailableMedia().
Figure 6: Using a media provider.
Media Search Engine Implementation
Our jukebox project includes a CIInetMediaProvider class. This IInetMediaProvider implementation uses several Windows Java coding techniques that are worth mentioning.
First, any Windows-deployed Java Web application that uses APIs from the java.net package will fail to operate for users that access the Internet through a proxy server. Many of the useful java.net.* services, such as the URL class that is used by our media search engine implementation, rely on java.net.Sockets to access data over the Internet. However, Java sockets fail to open for proxied clients unless the Java run time is configured to reflect their proxy settings. The coding trick to overcome this problem is to update the Java run-time System properties to consider the client's local system's proxy configuration before any Java Internet-related code is executed.
Figure 7 shows the CInetMediaProvider.initProvider code that sets the Java.System object proxy properties to the client's local proxy server settings, which are located in the Windows registry.
try
{
RegistryKey proxyKey =
Registry.CURRENT_USER.getSubKey(
"Software\\Microsoft\\Windows\\CurrentVersion\\Internet
Settings", true);
String proxyEntry =
(String) proxyKey.getValue("ProxyServer", "" );
int
i = proxyEntry.indexOf(":");
if
( i > 0 && i <
proxyEntry.length() - 1 )
{
String proxyServer =
proxyEntry.substring(0, i );
String proxyPort =
proxyEntry.substring( i+1, proxyEntry.length() );
System.getProperties().put( "proxySet", "true" );
System.getProperties().put( "proxyHost", proxyServer );
System.getProperties().put( "proxyPort", proxyPort );
}
}
catch ( Exception e )
{
System.out.println( "failed to set local proxy
settings." + e );
}
Figure 7: Enabling Java Internet API calls through proxy servers.
The Internet media search implementation core is surprisingly simple; a Java.net.URL object is created for each available media source. We parse the URL's raw HTML line-by-line by creating a java.io.BufferedReader around its input stream.
Next, each line is processed to extract new media sources and actual media files URLs using a private helper method called pareseHTMLLine. Thrown URLs and streams-related Java run-time exceptions are ignored because we don't care about broken links. This algorithm is demonstrated in Figure 8.
while (!m_lookupURLs.isEmpty() )
{
try
{
URL msource = new URL( (String) m_lookupURLs.firstElement() );
m_currHost =
msource.getHost();
BufferedReader in = new BufferedReader(
new InputStreamReader(msource.openStream()));
String inputLine;
while ( (inputLine = in.readLine()) != null )
parseHTMLLine(
inputLine );
in.close();
}
catch
( java.net.UnknownHostException e ){}
catch
( java.net.MalformedURLException e ){}
catch
( java.io.IOException e ){}
m_lookupURLs.removeElementAt( 0 );
}
Figure 8: Searching the Web for media files.
Putting the Pieces Together
The discussion below enumerates the major steps needed to complete the jukebox VJ6 solution. You may find it useful to download and browse the completed solution files that are available electronically at http://www.dromology.com/articles/jukebox.htm before continuing to read this section.
First, we need to create a new VJ6 control solution and project. Next, it's useful to rename the control's Java file and class name to a more meaningful name than the generic Control1. This class will be used for our jukebox control.
To this project, we need to add the mediaFinder Java package source files that include the media search engine public interfaces, and the media provider implementation we mentioned in the previous section.
Now we can step into the Form Designer of our jukebox control, set its basic properties, such as color and size, and add, name, and format the two play-set selection radio buttons (Play new 12" and Play All 12") and the application's title.
The next phase is the "manual labor" stage of the implementation - adding the application logic code to the jukebox control's Java source code. We need to declare only three member variables: one for the application state, one for its IInetMediaProvider, and one for its search thread. The rest of the members are automatically declared in the Java Visual WFC control initForm method by code generated by the Form Designer.
Now we're ready to add the playback presentation layer and playback logic to our jukebox. For this, we need to add the playback component to our jukebox control. The steps are similar for both controls, but we'll use the RealPlayer control for the rest of this discussion. For this task, we can use the VJ6 Toolbox customization feature. We need to locate the RealPlayer ActiveX control and check its checkbox in the Customize Toolbox dialog box (Tools | Customize Toolbox).
Next, we can access the Toolbox, click on the RealPlayer icon on the WFC Controls page, and draw a new control instance in our jukebox control's form. This operation causes VJ6 to generate Java classes and interfaces for all the methods, properties, and events of the playback control. These files are automatically added to our solution in a generated rmoc3260 Java package.
Now it's time to finish the application logic by implementing the control's state machine states and events. For this task, we simply need to wire together our state machine's events (see Figure 3) to events handlers and to write the resulting state changes and application behavior. The following discussion highlights some of the involved activities.
Event 1 is fired when the control starts running so we can implement it in the control's constructor. Here, we must initialize a media provider to fetch the hip-hop records found at the online store's Web site and set our state machine's state to idle play mode (state 0). The actual search is initiated by the application logic in the fetchMediaUrls private helper method.
To handle Event 2, our application logic must implement IMediaObserver and register itself as an observer of its media provider. In practice, we must perform the transition of our application state from idle mode (state 0) to play mode (state 1) if we are in idle mode in our IMediaObserver.onNewMediaAvailable implementation.
Handling Event 3 is straightforward. The playback logic knows when a media file has finished playing. We can select the RealPlayer control in the Form Designer, select to view events in the Properties window, and double-click to the right of the onPlayStateChanged event tab. This generates a new event handler method in our control's Java source code and wires the PlayStateChanged event to this method for us. In the method's body, we simply poll our media provider for the next available media in the current play set, and play it if the current media has finished playing, or if a playback error had occurred. Both conditions are recognizable by checking if the value PlayStateEvent.newState is 0.
Next, and last, is Event 4. We can locate the Click event tab in the Properties window of the Play new 12" and Play All 12" radio buttons, and by double-clicking on its right.
In the generated event handler, we must first handle the radio buttons' state, stop playback, set a media source that corresponds to the user's selected play set on our media provider, and perform a transition to idle play mode (state 0).
Implementation Disadvantages
We could have done a better job of reusing resources in our jukebox implementation; it doesn't reuse its search engine thread, nor does it cache media files for a play set, so repeated user selection initiates redundant Internet searches. However, our jukebox's educational and rather simplistic design and code can be easily modified to overcome these code shortcuts. Consider this an exercise for the reader.
Jukebox Internet Deployment
The jukebox is a WFC UserControl component that is embedded in an HTML Web page. This is by far the RADest option for deploying an IE-based Web application with VJ6.
First, we need to instruct VJ6 to compile the control as a COM class. For this, we need to checkmark the control's main class on the COM Classes page of our jukebox VJ project properties.
Now we're ready to choose the packaging for our COM class. The jukebox control can be packaged in an .ocx or .dll binary format, or in a .cab file that includes all the appropriate Java class files and project resources. Packaging the jukebox in a binary .ocx or .dll requires our users to change their security settings to at least prompt them to execute un-trusted ActiveX objects. Choosing the .cab file approach has several advantages. One is that the file can be digitally signed with a software publisher certificate so it may be executed on trusting users PCs without compromising Internet security.
In addition, the .cab file is much smaller (45KB compared to 134KB for the .ocx option), so its faster download time provides a better end-user experience.
Both packaging options self-register the jukebox COM component. However, it's important to remember that end users must have the COM-aware MS JVM installed on their PC to use any packaging options. This may be counter-intuitive to the ratio of a Windows programmer who may wrongly presume that the DLLs and OCXs that VJ6 produces are self-executable machine code components.
Both packaging options include the VJ6-generated .class files for our third-party media playback component. However, they need not - and do not - include the playback component's binary code. The COM class IDs in these class files allow the client's local MS JVM to dynamically bind Java method calls to the actual playback COM component bits, assuming that the playback component is already installed on the end user's PC at run time.
We're now ready to create the HTML files that will launch our jukebox Web application. The application is executed in an HTML window that is automatically resized to fit the control's dimensions. This window can be launched from any other HTML page by calling the following JScript method:
function execJukeBox()
{
myWindow =
window.open("jukebox.htm", "", fullScreen=1);
}
The source code for our Web application's HTML page source code is listed in Figure 9. HTML uses the <OBJECT> tag to define the jukebox control window's title, size, location, and dimension. The object's class ID property must be copied from the class ID of our jukebox's .java source file. The WIDTH and HEIGHT properties are set to match the control's dimension properties, and the VIEWASTEXT property is set to prevent VJ6 from loading the control when editing this HTML source file in its HTML editor.
<HTML><HEAD><TITLE>FatBeats 12"
Jukebox</TITLE>
<script language = "jscript" >
function resizeWindow()
{
window.moveTo( (
window.screen.width - 280 ) / 2 ,
(
window.screen.height - 156 ) / 2 );
window.resizeTo( 278,
154 );
}
</script>
</HEAD>
<BODY onLoad="resizeWindow()" leftmargin=0 nowrap
rightmargin=0
bottommargin=0
topmargin=0 bgcolor=Black>
<OBJECT classid=clsid:C503EA35-F5CB-11D2-883A-EEF57CA49957
HEIGHT=120 WIDTH=266
ID=jukebox
CODEBASE="jukebox.ocx" VIEWASTEXT
style="LEFT: 0px;
TOP: 0px; HEIGHT: 120px; WIDTH: 266px;" >
</OBJECT>
</BODY></HTML>
Figure 9: Jukebox Web application HTML source code.
The CODEBASE property is set according to the packaging strategy we chose for our application. All we have left to do is to copy the HTML files and the packaged .ocx or .cab files to a Web server, and test our application using this codebase. It's important not to skip this testing phase because it ensures our COM application loads over the Web, and that a local jukebox COM component registered on our development machine during the development process is not used instead.
Internet Deployment Disadvantages
Deploying a Java-created ActiveX control forces our users to use a browser that supports ActiveX and a COM-aware JVM. In practice, this limits our user base to Windows PCs running IE 4.0 or later who chose to install the optional MS JVM browser component on their system. Alas, there is no mainstream Internet technology that provides a decent cross-platform solution for developing and deploying Web applications that go beyond the familiar but rather limited ASPs, CGIs, and HTML 3.2 forms-based solutions.
Conclusion
The programming techniques shown in this article can be used to implement and deploy other types of audio/visual Internet playback applications that search the Internet for specific media types. I hope that it also demonstrated how to take advantage of numerous VJ6 RAD features. However, there is still a lot of manual programming work involved, namely tasks that can be automated by a RAD Web application development environment. For example, generation of the host HTML files could have been done by VJ6 when an ActiveX control project is created in it.
In addition, the media playback control is tightly coupled to the jukebox control through the Form Designer, so it's difficult to encapsulate it. This encapsulation is desirable because it allows us to easily switch between the RealNetworks' and Microsoft's competing solutions. Loosening this coupling is possible, but it involves a rather tedious and manual process that is a good subject for another article.
You can download the jukebox Web application's VJ6 solution files from http://www.dromology.com/jukebox/jb.htm.
Aviv Eyal is a Java multimedia software developer. He holds a bachelors degree in Computer Science from the University of Tel-Aviv, Israel. His fields of interests include Internet programming, software components, and object-oriented technologies. He has extensive software teaching, consulting, and mentoring experience. He currently works on multimedia applications at Microsoft. Aviv can be reached via e-mail at mailto:aviveyal@microsoft.com.
Jukeboxing with the Windows Media Player
The Internet jukebox presented in this article uses the RealPlayer 6.0 ActiveX control for media playback. However, the Windows Media Player may be used to perform the same task (see Figure 4). There are subtle usage and implementation differences between these controls that necessitate minor modifications to our jukebox project code:
§ Setting a media source URL on the RealPlayer control using the setSource method will start playback if autoPlay is set. However, the Windows Media Player API requires you to set the source using the setFileName method, then call the play method.
§ Both controls provide a playback state notification mechanism through playStateChange events. In both controls, an event state of zero means that playback has stopped. However, RealPlayer raises this event when a playback error occurs, so it's not necessary for our jukebox's code to explicitly catch playback exceptions. In both cases, we simply want to play the next play set media. However, the Windows Media Player does not fire the stop event when a similar exception occurs. Instead, it throws a standard ComFailException from its play method, so our jukebox code must, in this case, explicitly test and catch the exception and initiate playback of the next media as if the erroneous media had finished playing.
§ The Windows Media Player properties must be tweaked a bit to make it suitable for our jukebox's audio-only playback interface because the default embedded control includes an area for video display.
The jukebox Web application VJ6 solution that uses the Windows Media Player and these modifications are available for download with the rest of this article's source code.
Copyright © 1999 Informant Communications Group. All Rights Reserved. • Send feedback to the Webmaster |