HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML
 

The Java Media Framework API allows developers to incorporate various media types into Java applets and applications, and supports the capture, transmission, playback and transcode of many types of audio and video. There's an implementation written in 100% Pure Java, so the code can be ported onto any supported Java platform.

Objectivity for Java is a cross-platform, scalable, industrial-strength, object-oriented database that runs on NT and most flavors of UNIX. One of its strengths is its ability to store, manage and retrieve large binary objects.

This article demonstrates that the two technologies work quite well together, and will show you how to:

  • Represent multimedia objects as persistence-capable classes in Objectivity.
  • Write adapters that permit these objects to be read as InputStreams.
  • Send the InputStreams to the JMF MediaPlayer.
Motivation
There are a number of advantages to storing multimedia data in an OODBMS rather than leaving them in flat files; many of them are related to containment and management issues. It's easy to add metadata (such as authors, actors, animators and producers) to a multimedia database object by simply adding data members to the classes that represent them. This also leads to the possibility of querying the database based on such criteria.

A single multimedia object may be broken up into smaller segments. This means that random access to a particular part of the video or audio clip is also possible. Modifications can be made and a history can be kept on who performed what changes and when. These types of activities would be difficult to do outside a database framework.

Because the data is represented as an object, it's also possible to take advantage of OO features of the Java language to manipulate a multimedia object the same way one would manipulate any other Java object. The different kinds of multimedia data can be represented as different subclasses with inherited and overridden methods (as we've done in this example).

Since Objectivity can easily manage large databases and store gigabyte-size multimedia objects in a single database file, it's an ideal storage medium for such data. Because it's a pure object-oriented database, there's no need to break up the data into relational tables and reassemble them into objects at retrieval time. Because it supports distributed databases, it's possible to distribute the multimedia data across multiple hosts, even replicate parts of the database at multiple sites, and still be able to back up, restore and install all of the stored multimedia data with a simple administrative tool. This allows Objectivity to manage the directory structure of the data so you don't have to.

Persistence-Capable Classes
The Schema
To store a large multimedia object, it makes sense to break it up into smaller chunks of data. This provides some flexibility in the way we might decide to stream the data out. The persistence-capable classes we use for this application are shown in Figure 1.

Figure 1
Figure 1:

The ooLibrary provides a name lookup facility for a collection of ooBlob, so we can store and retrieve many multimedia objects with this application. The ooBlob is a collection of ooBlobSeg objects, and each ooBlobSeg contains an array of bytes that represents a chunk of the raw binary data.

Each class that needs to be stored in the database must be defined as a persistent-capable class. Objectivity provides a semitransparent interface to the Java language, but because there's no pre- or postprocessor, certain things need to be done to the class to make it persistent-capable. First, each class must implement a persistent interface. The easiest way to accomplish this is to extend from ooObj, which implements the persistent interface properly. The getters must then call fetch() and the setters must call markModified(). Finally, all access paths to the data members must be restricted to going through these methods. While this does require some preparatory work to reuse preexisting classes, it permits Objectivity to run on all standard JVMs and development environments without any special customizations or patches.

Storage Hierarchy
Objectivity supports a distributed storage hierarchy with page-server architecture (see Figure 2). At the top level is a Federated Database that provides an entry point into the persistent storage. An FD is a collection of databases in the same sense that Star Trek's Federation of Planets is a collection of planets. Each database corresponds to a physical file on disk, but need not be physically located on the same host or network. A lock server process provides the locking services for the entire federation, and each client can talk directly to the page server host after it has received proper authorization from the lock server.

Figure 2
Figure 2:

Each database comprises a collection of containers that provides another level in our storage hierarchy. Each object must reside in a container, which in turn resides in a database, which resides in an FD. Containers and databases can be created and destroyed dynamically as easily as objects.

Persistent object references can refer to objects in other databases, so object navigation is still location-transparent across databases. Objectivity permits 65,535 databases per federation and 32,767 containers per database. The page limit per container is 65,535 logically addressable pages, but a large array that spans multiple pages requires only one logically addressable page and can span many more physical pages not subject to the page limit of a container. Since page size can be up to 64KB, this means that Objectivity can address and store petabytes of data without problems.

The locking granularity is at the container level. Since most multimedia objects can fit in a single container, storing each multimedia object in its own container makes perfect sense. This means that once a lock is obtained on the container, a client application can talk directly to the page server that holds the data and stream through the entire multimedia object without needing to stop in the middle to communicate with the lock server process. The programming example, which you can download from www.JavaDevelopersJournal.com, can be easily customized to support a larger segment size for larger multimedia objects, but some small modifications to the code would need to be made to allow for a multicontainer, multimedia object.

Streaming the Data
ooBlob has no streaming interface; it's just an aggregate of objects. However, it's fairly straightforward to write a wrapper for ooBlob, which does provide this interface. Class ooBlobStream derives from java.io.InputStream. Each instance holds onto an ooBlob reference and serves out data on demand to the user of the instance. ooBlobStream instances are normal transient Java objects so they're not part of the schema.

ooBlobStream doesn't do fancy double-buffering to ensure that there's always available data. The application has been tested on a Pentium II/233 and can play 25 minute MPEG files without any delays. It's possible that double-buffering may be necessary on some platforms and with some multimedia types, but it seems that JMF media player does all the buffering that's required to stream the data smoothly on data that's been tested so far.

What's really nice about the InputStream interface, however, is that you can either wrap a java.io.BufferedInputStream around it, which may provide better performance, or write your own multithreaded double-buffered input stream, ensuring that there's always data available to be read. Such a class could be quite general-purpose and need not be aware of the fact that the stream it's buffering is coming from Objectivity.

Thread Policies
Objectivity supports multithreaded clients, but it also supports multiple database sessions per process. It may be desirable to have a strict mapping of threads to sessions, where each thread belongs to a session. This is called the "restricted thread policy." If we use this, it means that each thread that accesses persistent data must be explicitly joined to a session before it can proceed, and each thread is joined to one session at a time at most. In our ooBlobSeg code most of the accessors do explicitly join to the session. The reason for this is that, in this particular multimedia application, we have no control over which threads access our data. Perhaps in another application with multiple concurrent open sessions, explicitly joining every incoming thread to a session is either inconvenient or impractical. Thus it may make more sense to use the "unrestricted thread policy," which would allow a thread to implicitly and automatically join the session that owns the persistent object being accessed.

Java Media Framework API
JMF provides a platform-neutral framework for displaying time-based media. The Java Media Player APIs are designed to support most standard media content types, such as MPEG-1, MPEG-2, QuickTime, AVI, WAV, AU and MIDI. The API offers a platform-neutral interface for writing multimedia applications.

JMF doesn't directly support reading from InputStream, so there's still a bit of work to be done before we can play the multimedia objects directly from persistent store. First we must create a class that implements PullSourceStream and, optionally, the seekable interface to provide random access to the multimedia object. The JMFSourceStream wraps an InputStream, a ContentDescriptor and a long that represents the size of the stream.

Next we must create a class that derives from PullDataSource and properly wraps the PullSourceStream. Once done, we can pass this on to the JMF MediaPlayer and play our multimedia object in real time.

Conclusion
Objectivity provides a convenient and high-performance storage mechanism for multimedia files because of its page server architecture. Streaming data in real time is handled quite well without any extra double-buffering.

It took approximately four person-days to write the code that stores and plays multimedia data from Objectivity. I had to learn the JMF API and its limitations during this time. The source code is readily available for download from www.JavaDevelopersJournal.com.

Acknowledgments
I'd like to thank Chad Adams from Payback Training Systems for his assistance in helping me understand the JMF API and Java Multimedia Players in general. I'd also like to thank Al Cherri at Objectivity for his comments.

Author Bio
S. Alan Ezust is a senior systems engineer at Objectivity Inc. (www.objectivity.com), a leading provider of scalable, mission-critical, object-oriented databases.
He can be reached at: [email protected]

Download Assoicated Source Files (Zip format - 27.2 KB)

 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.