Synchronized Auditory and Visual Content
An Introduction with Examples in Java |
Prof. David Bernstein |
Computer Science Department |
bernstdh@jmu.edu |
Player
Classimport java.awt.*; import java.awt.event.*; import java.io.*; import java.net.*; import javax.media.*; import javax.swing.*; /** * A JPanel that contains a media player * (using the Java Media Framework) * * @version 1.0 * @author Prof. David Bernstein, James Madison University * */ public class PlayerPanel extends JPanel implements ControllerListener { protected boolean includeVisual, includeControls; protected Player player; /** * Constructor * */ public PlayerPanel(boolean includeVisual, boolean includeControls) { player = null; this.includeVisual = includeVisual; this.includeControls = includeControls; setLayout(new BorderLayout()); } /** * Load a media file * * @param fn The name of the file */ public void loadFile(String fn) { Component c; URL url; try { url = new URL("file:"+ System.getProperty("user.dir")+ "/"+ fn); player = Manager.createPlayer(url); player.addControllerListener(this); player.start(); } catch (MalformedURLException mue) { JOptionPane.showMessageDialog(this, "Unable to open file!"); } catch (NoPlayerException npe) { JOptionPane.showMessageDialog(this, "Unable to create a player!"); } catch (IOException ioe) { JOptionPane.showMessageDialog(this, "Unable to connect to source!"); } } /** * Handle controllerUpdate events * (required by ControllerListener) * * @param evt The ControllerEvent */ public synchronized void controllerUpdate(ControllerEvent evt) { Component c; if (evt instanceof RealizeCompleteEvent) { c = player.getVisualComponent(); if ((c != null) && (includeVisual)) { add(c, BorderLayout.CENTER); } c = player.getControlPanelComponent(); if ((c != null) && (includeControls)) { add(c, BorderLayout.SOUTH); } // Force a re-layout validate(); } } }
import java.awt.*; import javax.swing.*; /** * An example that uses a media player * * @version 1.0 * @author Prof. David Bernstein, James Madison University * */ public class PlayerPanelDriver { /** * The entry point of the example * * @param args The command line arguments */ public static void main(String[] args) { boolean includeVisual; JFrame f; Container contentPane; PlayerPanel p; f = new JFrame(); contentPane = f.getContentPane(); contentPane.setLayout(new BorderLayout()); if (args[0].indexOf(".mpg") >= 0) includeVisual = true; else includeVisual = false; p = new PlayerPanel(includeVisual, true); contentPane.add(p, BorderLayout.CENTER); f.setSize(320,300); f.setVisible(true); p.loadFile(args[0]); } }
Processor
States in JavaTrackControl
objectsProcessor
States in Java (cont.)
import java.awt.*; import javax.swing.*; /** * An example that uses a media processor * to play content * * @version 1.0 * @author Prof. David Bernstein, James Madison University * */ public class ProcessorPanelDriver { /** * The entry point of the example * * @param args The command line arguments */ public static void main(String[] args) { JFrame f; Container contentPane; ProcessorPanel p; f = new JFrame(); contentPane = f.getContentPane(); contentPane.setLayout(new BorderLayout()); p = new ProcessorPanel(); contentPane.add(p, BorderLayout.CENTER); f.setSize(320,300); f.setVisible(true); p.loadAndStart(args[0]); } }
import java.awt.*; import java.awt.event.*; import java.io.*; import java.net.*; import javax.media.*; import javax.swing.*; /** * A JPanel that contains a media processor * (using the Java Media Framework) * * @version 1.0 * @author Prof. David Bernstein, James Madison University * */ public class ProcessorPanel extends JPanel implements ControllerListener { protected boolean transitionOK; protected Object synchronizer; protected Processor processor; /** * Constructor * */ public ProcessorPanel() { processor = null; synchronizer = new Object(); transitionOK = true; setLayout(new BorderLayout()); } /** * Get the Processor associated with this ProcessorPanel * * @return The Processor */ public Processor getProcessor() { return processor; } /** * Configure the Processor * * @return true of the Processor was realized */ public boolean configure() { boolean status; status = false; // Put the Processor into the configured state processor.configure(); if (waitFor(processor.Configured)) { // Set the content descriptor to null so that the // processor can be used as a player processor.setContentDescriptor(null); status = true; } else { JOptionPane.showMessageDialog(this, "Unable to configure the processor!"); } return status; } /** * Handle controllerUpdate events * (required by ControllerListener) * * @param evt The ControllerEvent */ public synchronized void controllerUpdate(ControllerEvent evt) { if ( (evt instanceof ConfigureCompleteEvent) || (evt instanceof RealizeCompleteEvent) || (evt instanceof PrefetchCompleteEvent) ) { // Synchronize this block synchronized(synchronizer) { transitionOK = true; synchronizer.notifyAll(); } } else if (evt instanceof ResourceUnavailableEvent) { // Synchronize this block synchronized(synchronizer) { transitionOK = false; synchronizer.notifyAll(); } } } /** * Load and start a media file * * @param fn The name of the file */ public void loadAndStart(String fn) { boolean status; status = loadFile(fn); if (status) status = configure(); if (status) status = realize(); } /** * Load a media file * * @param fn The name of the file * @return true if the process completed successfully */ public boolean loadFile(String fn) { boolean status; URL url; status = false; try { url = new URL("file:"+ System.getProperty("user.dir")+ "/"+fn); if (url != null) { processor = Manager.createProcessor(url); processor.addControllerListener(this); status = true; } } catch (NoProcessorException npe) { JOptionPane.showMessageDialog(this, "Unable to create a processor!"); } catch (IOException ioe) { JOptionPane.showMessageDialog(this, "Unable to connect to source!"); } return status; } /** * Realize the Processor * */ public boolean realize() { boolean status; Component c; status = false; // Put the Processor into the realized state processor.prefetch(); if (waitFor(processor.Prefetched)) { // The Processor has been realized so the // components can be layed out c = processor.getVisualComponent(); if (c != null) add(c, BorderLayout.CENTER); c = processor.getControlPanelComponent(); if (c != null) add(c, BorderLayout.SOUTH); validate(); // Start the Processor processor.start(); status = true; } else { JOptionPane.showMessageDialog(this, "Unable to realize the processor!"); } return status; } /** * Block the thread of execution until the Processor has * transitioned into the given state. * * @param state The state to wait for * @return false if the transition failed, true otherwise */ protected boolean waitFor(int state) { // Make this block synchronized synchronized(synchronizer) { try { while ((processor.getState() != state) && (transitionOK)) { synchronizer.wait(); } } catch (InterruptedException ie) { // Ignore it } } return transitionOK; } }
import java.awt.*; import java.awt.image.*; import java.awt.event.*; import java.util.*; import javax.media.*; import javax.media.renderer.*; import javax.media.format.*; /** * A Canvas that implements the VideoRenderer interface * and, hence, can be used to display video. * * This particular implementation can only be used with RGB * .mov files * * @version 1.0 * @author Prof. David Bernstein, James Madison University */ public class RGBRenderer implements VideoRenderer { protected boolean started; protected Buffer lastBuffer; protected Component component; protected int inputHeight, inputWidth; protected Format[] supportedFormats; protected Image destImage; protected MemoryImageSource sourceImage; protected Rectangle bounds; protected RGBFormat inputFormat, supportedRGB; protected static final int rMask = 0x000000FF; protected static final int gMask = 0x0000FF00; protected static final int bMask = 0x00FF0000; /** * Constructor */ public RGBRenderer() { lastBuffer = null; component = null; bounds = null; started = false; inputWidth = 0; inputHeight = 0; // Prepare supported input formats and preferred format supportedRGB = new RGBFormat(null, // size Format.NOT_SPECIFIED, // maxDataLength int[].class, // buffer type Format.NOT_SPECIFIED, // frame rate 32, // bitsPerPixel rMask, gMask, bMask, // component masks 1, // pixel stride Format.NOT_SPECIFIED, // line stride Format.FALSE, // flipped Format.NOT_SPECIFIED // endian ); supportedFormats = new VideoFormat[1]; supportedFormats[0] = supportedRGB; } /** * Close the Renderer (required by VideoRenderer) */ public void close() { // Do nothing } /** * Get the portion of the Component being used by * this Renderer (required by VideoRenderer) * * @return The Rectangle used (null for the whole Component) */ public Rectangle getBounds() { return bounds; } /** * Get the Component that this Renderer is * using (required by VideoRenderer) * * @return The Component */ public Component getComponent() { if (component == null) { component = new RendererCanvas(this); } return component; } /** * Get the Controls for this renderer * (required by VideoRenderer) * * Since this implementation does not support Controls it * returns an empty array. * * @return The controls */ public Object[] getControls() { return (Object[]) new Control[0]; } /** * Get a Control based on a control type * (required by VideoRenderer) * * Since this implementation does not support Controls it * returns an empty array. * * @return The controls */ public Object getControl(String controlType) { return null; } /** * Get the name of this Renderer * (required by VideoRenderer) * * @return The name */ public String getName() { return "RendererCanvas"; } /** * Get the size of the input data * * @return The size (in pixels) */ public Dimension getSize() { return new Dimension(inputWidth, inputHeight); } /** * Get the array of supported formats * (required by VideoRenderer) * * @return The array of supported formats */ public Format[] getSupportedInputFormats() { return supportedFormats; } /** * Open this Renderer (required by VideoRenderer) * */ public void open() throws ResourceUnavailableException { sourceImage = null; destImage = null; lastBuffer = null; } /** * Paint the current image * * @param g The Graphics context */ public void paintImage(Graphics g) { Rectangle tempBounds; if ( (g == null) || (destImage == null) || (component == null)) return; if (bounds == null) { tempBounds = component.getBounds(); tempBounds.x = 0; tempBounds.y = 0; } else { tempBounds = bounds; } g.drawImage(destImage, tempBounds.x, tempBounds.y, tempBounds.width, tempBounds.height, 0, 0, inputWidth, inputHeight, component); } /** * Process the input data and render it * * @param buffer The input data * @return BUFFER_PROCESSED_OK if successful */ public synchronized int process(Buffer buffer) { Object data; Format inf; Graphics g; // Initial error checking if (component == null) return BUFFER_PROCESSED_FAILED; inf = buffer.getFormat(); if (inf == null) return BUFFER_PROCESSED_FAILED; if ( (inf != inputFormat) || (!buffer.getFormat().equals(inputFormat)) ) { if (setInputFormat(inf)!=null) { return BUFFER_PROCESSED_FAILED; } } data = buffer.getData(); if (!(data instanceof int[])) { return BUFFER_PROCESSED_FAILED; } // Check to see if the data have changed if (lastBuffer != buffer) { lastBuffer = buffer; buildImage(buffer); } // Render the image sourceImage.newPixels(0, 0, inputWidth, inputHeight); g = component.getGraphics(); if (g != null) paintImage(g); return BUFFER_PROCESSED_OK; } /** * Reset the Renderer (required by VideoRenderer) * */ public void reset() { // Do nothing } /** * Set the portion of the Component to use * (required by VideoRenderer) * * @param rect The area to use (null for the whole Component) */ public void setBounds(Rectangle bounds) { this.bounds = bounds; } /** * Set the Component that the Renderer should * use (required by VideoRenderer) * * @return false if the Renderer can't use the Component */ public boolean setComponent(Component comp) { component = comp; return true; } /** * Set the input format (required by VideoRenderer) * * @param format The Format of the input data * @return The Format that was set or null */ public Format setInputFormat(Format format) { Dimension size; Format retval; if ( (format != null) && (format instanceof RGBFormat) && (format.matches(supportedRGB)) ) { inputFormat = (RGBFormat)format; size = inputFormat.getSize(); inputWidth = size.width; inputHeight = size.height; retval = format; } else { retval = null; } return retval; } /** * Start the Renderer (required by VideoRenderer) */ public void start() { started = true; } /** * Stop the Renderer (required by VideoRenderer) */ public void stop() { started = false; } /** * Build a new image from the input data * * @param buffer The input data */ protected void buildImage(Buffer buffer) { DirectColorModel colorModel; Object data; RGBFormat format; data = buffer.getData(); if (!(data instanceof int[])) return; format = (RGBFormat) buffer.getFormat(); colorModel=new DirectColorModel(format.getBitsPerPixel(), format.getRedMask(), format.getGreenMask(), format.getBlueMask()); sourceImage=new MemoryImageSource(format.getLineStride(), format.getSize().height, colorModel, (int[])data, 0, format.getLineStride()); sourceImage.setAnimated(true); sourceImage.setFullBufferUpdates(true); if (component != null) { destImage = component.createImage(sourceImage); component.prepareImage(destImage, component); } } }
import java.awt.*; /** * A Canvas that is used by an RGBRenderer to display video. * * This particular implementation can only be used with an * RGBRenderer. * * @version 1.0 * @author Prof. David Bernstein, James Madison University */ public class RendererCanvas extends Canvas { RGBRenderer renderer; /** * Constructor * * @param renderer The RGBRenderer that determines the size */ public RendererCanvas(RGBRenderer renderer) { this.renderer = renderer; } /** * Get the minimum size of this Canvas */ public Dimension getMinimumSize() { return renderer.getSize(); } /** * Get the preferred size of this Canvas */ public Dimension getPreferredSize() { return getMinimumSize(); } /** * Update this Component * * @param g The Graphics context */ public void update(Graphics g) { // Do nothing } /** * Paint this Component * * @param g The Graphics context */ public void paint(Graphics g) { // This method needs to handle repaints when the // movie is paused renderer.paintImage(g); } }
import java.awt.*; import javax.media.*; import javax.media.control.*; import javax.media.format.*; import javax.swing.*; /** * An example that uses a custom VideoRenderer * * @version 1.0 * @author Prof. David Bernstein, James Madison University * */ public class RendererDriver { /** * The entry point of the example * * @param args The command line arguments */ public static void main(String[] args) { boolean status; JFrame f; Container contentPane; int i; ProcessorPanel p; RGBRenderer renderer; TrackControl videoControl; TrackControl[] trackControls; f = new JFrame(); contentPane = f.getContentPane(); contentPane.setLayout(new BorderLayout()); p = new ProcessorPanel(); contentPane.add(p, BorderLayout.CENTER); status = false; status = p.loadFile(args[0]); status = p.configure(); if (status) { status = false; // Get the video track control (if there is one) trackControls = p.getProcessor().getTrackControls(); videoControl = null; for (i=0; i < trackControls.length; i++) { if (trackControls[i].getFormat() instanceof VideoFormat) { videoControl = trackControls[i]; break; } } if (videoControl != null) { // Set the Renderer for the video track control try { renderer = new RGBRenderer(); videoControl.setRenderer(renderer); status = true; } catch (UnsupportedPlugInException upie) { System.out.println("Unsupported PlugIn"); } } } if (status) { p.realize(); } f.setSize(320,300); f.setVisible(true); } }
import java.awt.*; import javax.media.*; /** * An RGBRenderer that "pops-up" text * * This particular implementation can only be used with RGB * .mov files * * @version 1.0 * @author Prof. David Bernstein, James Madison University */ public class PopUpRenderer extends RGBRenderer { protected int index, frame; protected int[] duration, frames; protected String[] text; /** * Constructor * * @param frames Array of frame numbers for popups * @param text Array of text for popups * @param duration Array of durations (in frames) for popups */ public PopUpRenderer(int[] frames, String[] text, int[] duration) { this.frames = frames; this.text = text; this.duration = duration; frame = -1; index = 0; } /** * Paint the current image * * @param g The Graphics context */ public void paintImage(Graphics g) { // This should be buffered to avoid flicker super.paintImage(g); if (index < frames.length) { if ((frame >= frames[index]) && (frame <= frames[index]+duration[index])) { g.setColor(Color.white); g.fillRect(50,50,150,25); g.setColor(Color.black); g.drawString(text[index], 70, 70); if (frame == frames[index]+duration[index]) index++; } } } /** * Process the input data and render it * * @param buffer The input data * @return BUFFER_PROCESSED_OK if successful */ public synchronized int process(Buffer buffer) { frame++; return super.process(buffer); } }
import java.awt.*; import javax.media.*; import javax.media.control.*; import javax.media.format.*; import javax.swing.*; /** * An example that uses a custom VideoRenderer that * includes "pop-up" text * * @version 1.0 * @author Prof. David Bernstein, James Madison University * */ public class PopUpRendererDriver { /** * The entry point of the example * * @param args The command line arguments */ public static void main(String[] args) { boolean status; JFrame f; Container contentPane; int i; int[] frames, duration; ProcessorPanel p; PopUpRenderer renderer; String[] text; TrackControl videoControl; TrackControl[] trackControls; text = new String[2]; text[0] = "Nice Mask!"; text[1] = "Did he say organisms?"; frames = new int[2]; frames[0] = 70; frames[1] = 135; duration = new int[2]; duration[0] = 10; duration[1] = 20; f = new JFrame(); contentPane = f.getContentPane(); contentPane.setLayout(new BorderLayout()); p = new ProcessorPanel(); contentPane.add(p, BorderLayout.CENTER); status = false; status = p.loadFile(args[0]); status = p.configure(); if (status) { status = false; // Get the video track control (if there is one) trackControls = p.getProcessor().getTrackControls(); videoControl = null; for (i=0; i < trackControls.length; i++) { if (trackControls[i].getFormat() instanceof VideoFormat) { videoControl = trackControls[i]; break; } } if (videoControl != null) { // Set the Renderer for the video track control try { renderer = new PopUpRenderer(frames, text, duration); videoControl.setRenderer(renderer); status = true; } catch (UnsupportedPlugInException upie) { System.out.println("Unsupported PlugIn"); } } } if (status) { p.realize(); } f.setSize(320,300); f.setVisible(true); } }