Contents
Foreword
3082 lines of codes further I finally have a program running that reflects a month worth of programming, looking through API’s and testing code. Hoping that at the same time it hides the frustration that all of this also brought (is a program ever bug-free?).
As with almost all things people start, or at least, I feel they should; they start optimistic. All the time in the world to master this Java Media Framework package thing! About 3.5 weeks later and some illusions less, you update your documentary and realize that 3.5 weeks was not as much as you actually thought. Nevertheless I must say that I really enjoyed coding this program, doing the imaging, creating the test movies and fiddle around with sound software.
I’m happy I took up a bachelor project in the multimedia and culture corner, considering that after my bachelor I will continue studying digital graphic design and interactive media. The manipulation of video and audio files will undoubtedly be a part of this and I feel programming this program as my bachelor project was the first small step in a, for me, important direction.
I’d like to thank Dhr. Eliens for giving me this opportunity.
The Idea(s)
After the first orientation, I wanted to create a Media Library, containing the basic 3 media types, images, sound and movies. I wanted an alternative way to navigate through stored media, so an experimental design came to mind. First thoughts went out to a "zoom browser", in my mind it would display the stored media in a selected directory. Subdirectories in the directory would be linked to the base directory and displayed with contents (or partially) at the same time (Figure 1)
Zooming to a specific directory would enlarge the detail of that directory, displaying more of the media inside it. Giving, in my opinion, a good overview while navigating through media libraries/directories. I started coding the needed Swing components for loading directories, detecting its contents (with file type filters) running into memory problems with loading big directories of large images, since they are all completely loaded. The way out here, would be to load small previews of the images, before implementing this - note that the displaying of one directory would look quite similar to Windows Explorer in Thumbnails mode - coding this part took me quite a while and when I wanted to have the "first frame preview" of video files (avi/mpg) I first bumped into the Java Media Framework. Realizing that this actually was the needed-to-explore part, I dove into the Sample Programs, API’s and Tutorials. I found out that it wasn’t as related to Swing as initially thought.
I dove into the Java Media Framework and after another 3 weeks of hard work, I can present the result as the final compiled program together with this document.
Timetable
Week 1
Week 2
Week 3
Week 4
Java Media Framework
A Simple Video Player
Implementing File
:Executing the File:
"The Java Media Framework API (JMF) enables audio, video and other time-based media to be added to Java applications and applets." [1] Java Media players support a six-state model based on the two fundamental states Stopped and Started. [2]
A video file player is created by the statement:
Player player = Manager.createPlayer(url);
Executing this statement will cause the player to be in the Unrealized state. Before being able to play the file, we need to go through all the transitions in Figure 2. To handle the states we need a Controller. This is being done by adding a ControllerListener to the player:
player.addControllerListener((ControllerListener) x);
A ControllerListerner is an interface forcing the implementing class to have the function controllerUpdate(ControllerEvent controllerEvent). This is the function that will catch the states of the player. Now we can execute:
player.realize();
In the JMF API we find this explanation of the method realize():
Constructs the media dependent portions of the Controller. This may include examining media data and might take some time to complete. The realize method puts the Controller into the Realizing state and returns immediately. When realize is complete and the Controller is in the Realized state, the Controller posts a RealizeCompleteEvent
.The RealizeCompleteEvent is caught by the ControllerListener, which can then give the instruction to prefetch the data in the giving url:
player.prefetch();
In the JMF API we find this explanation of the method prefetch():
Processes as much data as necessary to reduce the Controller's start latency to the shortest possible time. This typically involves examining media data and takes some time to complete. The prefetch method puts the Controller into the Prefetching state and returns immediately. When Prefetching is complete and the Controller is in the Prefetched state, the Controller posts a PrefetchCompleteEvent.
After this the player is ready loading the file and can be started:
player.start();
The default controls in the bottom of the player are simply grabbed from the player (they are part of the package) by executing:
Component visual = mplayer.getVisualComponent();
And adding them to the Panel
getContentPane().add(visual);
Java Media Framework
A Simple Video Frame Navigator
Implementing File:
Executing the File:
The Video Seeker doesn’t "play" the video file, but in stead the video can be accessed per frame. Moving forward or backward 1 frame or going to a Random Frame. It follows the same first steps as the Simple Video Player.
Player player = Manager.createPlayer(url);
player.addControllerListener((ControllerListener) x);
player.realize();
This time after the realize() function, it has the Program actively waiting for the player to finish realizing, so it can make sure that statements afterwards needing the playerinfo get the correct data. The active waiting is done by:
waitForState(player.Realized)
It then can safely get the FramePositioningControl from the player and convert the duration-time of the file to frames:
FramePositioningControl fpc = (FramePositioningControl) player.getControl("javax.media.control.FramePositioningControl");
int totalFrames = fpc.mapTimeToFrame(player.getDuration());
It then prefetches and actively waits in a similar fashion as above:
player.prefetch();
waitForState(player.Prefetched)
All the data is now ready for playing and we have a control fpc that enables us to seek frames, by the following commands:
if (command.equals("Forward")) {
fpc.skip(1);
} else if (command.equals("Backward")) {
fpc.skip(-1);
} else {
randomFrame = fpc.seek((totalFrames * Math.random());
}
Java Media Framework
The Advanced Video Player
Implementing Files
:concerning the Main player code:
- AdvancedVideoPlayer.java
- AdvancedVideoPlayerWindow.java
- AcceptCodecs.java
concerning the GUI interface:
- WireFrameLayout.java
- ControlBarPictureInterface.java
concerning the Effect window implemenation:
- EffectBar.java
- EffectPanel.java
- InstalledEffects.java
- RunningEffect.java
concerning runnable effects:
- MidZoomEffect.java
- RotationEffect.java
- UpsideDownEffect.java
- WhatEverEffect.java
Executing the File:
or directly:
The Advanced Video Player is more complex. It is different from a default Player in a number of ways. Firstly, the most noticeable (visual) difference with the default Player is it’s Custom Control GUI. The GUI is a picture implemented by a ControlBarPictureInterface, which specifies the filename of the picture and where the Controlcoordinates are located inside the picture. This way, it would be easy to create a different picture and just have it implement the ControlBarPictureInterface in Java (take a look at WireFrameLayout.java to see the implementation of an interface). Having a MouseListener catching the coordinates of clicks in the picture, and comparing them to the coordinates specified in the ControlBarPictureInterface-implementation will give the actions needed to execute. This means you are free to make any GUI you want yourself.
The Advanced Video Player offers the ability to render effects over the video stream. In order to do this, there is an essential difference with the Simple Player. In stead of creating a Player out of the selected video file, it creates a Processor:
Processor processor = Manager.createProcessor(new MediaLocator(url));
The Processor Interface actually extends the Player Interface. In the JMF API we find this explanation why this is needed:
Processor extends the state transition cycle of a Player by adding the Configuring and Configured states. The purpose of these additional states is to further refine the realizing process. The realizing step is essentially split into two phases:
Between these two steps, you can program the Processor to perform specific processing on its media stream.
So we get the new State Transition Diagram:
So we configure the processor:
player.configure();
waitForState(player.Configured)
Now, it is worth noting that a Processor is not just there to play the data, it can take its input (the video-file) and create any content output. We want to use it as a player in the end. So we say this by executing:
player.setContentDescriptor(null);
(By giving null as a parameter, we say we want to use it as a player. The function setContentDescriptor actually takes a ContentDiscriptor-Object as it’s parameter)
In order to render effects over the video, we need to grab the VideoTrack from the videofile. A videofile can contain multiple tracks (video/audio) – so we need to get the tracks and find the video-track:
TrackControl tc[] = processor.getTrackControls();
for (int i = 0; i < tc.length; i++) {
if (tc[i].getFormat() instanceof VideoFormat) {
TrackControl videoTrack = tc[i];
} }
In order to render Effects over the videoTrack, we should have an array of Codecs. A Codec is a media processing unit that accepts a Buffer object as its input, performs some processing on the input data, and then puts the result in an output Buffer object. The Codec array is initially empty, but can only be filled by the EffectPanel, we’ll get to that later. Applying the (empty) Codec array to the videoTrack is done by the following 2 statements:
Codec effectChain[] = { };
videoTrack.setCodecChain(effectChain);
After this the processor is ready to be realized and prefetched. Note that processor.realize() is not being executed explicitly, just specifying processor.prefetch() is enough, because the processor can not skip any of the states in Figure 3, so prefetch() will also realize it before it prefetches.
In order to render an effect over the VideoTrack, the Window implements the AcceptCodecs-Interface, it just says the window should implement the function:
public void startEffectChain(Codec[] codec)
This way the EffectPanel can call this function to reload the Processor with the new Codec Array. I was hoping to do this real-time, so with no need to restart the video-file, but this appeared not to be possible; the processor must be in the Configured state in able for it to execute the videoTrack.setCodecChain(effectChain) function and since a started or stopped processor is in respectively, the Started or Prefetched state, we have an impossibility.
The EffectPanel is simple, it has two lists; one with the Available Effects (defined in the InstalledEffects.java file) and one with the currently list of running effects. Whenever the user adds an effect, by clicking the Add-Button, it updates the Running Effects-list, add the newly added Effect to the Codecchain and finally sends this updated Codecchain to the Processor, so it can restart the video-file with its new chain.
Limitations and Future Upgrades
This list could go on for ages, but these were the functionalities that had already crossed my mind when writing the final documentation.
Conclusion
Although not explored to the fullest, the Java Media Framework feels like a solid package of tools. It’s the first package for Java that enables the programmer to play and manipulate time-based-media. Everything in the package makes sense, the build-up, state-flow diagrams, implementation interfaces, etc. However, the problem with the JMF, as I run into more often with Java, is that it feels slow. Faster response times would help make the programmed code feel more responsive;we all know that manipulating time-based-media is a CPU intense process, but I can’t help feeling that it could run a lot smoother. Although I can’t bring any scientific proof for this, only experience with other programs could support this assumption in a small way.
Nevertheless I can recommend the Java Media Framework for this kind of programming, the transparency of the package is great and knowing that it’s possible to build the delivered program in such a short number of time, I know that the Java Media Framework will satisfy almost all of the basic time-based-media needs.
References
[1] Java Media Framework Homepage by Sun Microsystems – 1995-2003
http://java.sun.com/products/java-media/jmf/
[2] Java World by IDG Company – 2003
http://www.javaworld.com/javaworld/jw-04-1997/jw-04-jmf.html
[3] Java Media Framework API by Sun Microsystems – 1999-2001
http://java.sun.com/products/java-media/jmf/2.1.1/apidocs/