ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Streaming QuickTime with Java
Pages: 1, 2

Providing a Control GUI


            // Make monitor window
            startStopButton = new Button ("Start");
            configButton = new Button ("Configure");
            startStopButton.addActionListener (this);
            configButton.addActionListener (this);
            Frame monitorFrame = new Frame ("QTJ Streaming");
            monitorFrame.setLayout (new BorderLayout());
            Panel buttonPanel = new Panel();
            buttonPanel.add (startStopButton);
            buttonPanel.add (configButton);
            monitorFrame.add (buttonPanel, BorderLayout.SOUTH);
            monitorFrame.pack();
            monitorFrame.setVisible(true);

This sets up a trivial GUI for controlling and configuring the Presentation, basically just offering a start/stop button and a configure button. The buttons are referred to this as an ActionListener, meaning this class will need to provide an actionPerformed method later to handle button clicks. A screenshot of the control GUI is shown in Figure 3.


Figure 3. Monitor/control window

One interesting question you might ask at this point is: "Since when are we worrying about providing a server with a GUI?" Presumably, this is a holdover from the Classic Mac OS, which didn't have a command line to start applications and send arguments to them. But moreover, one would usually like to provide a preview of the streaming data, and if you're going to have a preview window, why not have a configuration GUI, too?

Anyways, this is probably moot, because QTJ 6.1 does not provide an AWT Component that you could use to provide the preview. Hopefully at some point, the QTFactory will get a new overload for makeQTComponent that takes a Presentation and returns a Component that shows the streaming video. It's probably possible to hack one up entirely in Java with some QuickDraw voodoo. If, in each task() callback (see below) you take the GWorld created earlier, convert it to a Pict, and write that as a uniquely named file, you'll see each one is different, meaning the GWorld is getting fresh data each time. So if you instead took the GWorld and could write its pixels to an AWT Component on each pass, you'd have an onscreen preview. Anyone game to try this? See you on the quicktime-java list.

Details


            // add shutdown handler to make sure presentation 
            // gets stopped
            Thread presentationStopper = new Thread() {
                    public void run() {
                        try {
                            pres.stop();
                        } catch (QTException qte) {}
                    }
                };
            Runtime.getRuntime().addShutdownHook (presentationStopper);

This shutdown hook makes sure the Presentation is stopped before the program exits. This is important because, like the SequenceGrabber, the Presentation will be happy to keep running after your application quits, tying up a port, burning cycles, keeping other applications from using your capture device, etc.


		} catch ( QTException e ) {
			e.printStackTrace();
			System.exit (-1);
		}
    }

Finally, the constructor catches any QTExceptions that may have been thrown along the way.


    public void actionPerformed (ActionEvent ae) {
        System.out.println ("actionPerformed");
        try {
            if (ae.getSource() == startStopButton) {
                if (broadcasting) {
                    pres.stop();
                    stopTasking();
                    broadcasting = false;
                    startStopButton.setLabel ("Start");
                    System.out.println ("Stopped");
                } else {
                    pres.start();
                    startTasking();
                    broadcasting = true;
                    startStopButton.setLabel ("Stop");
                    System.out.println ("Started");
                }
            } else if (ae.getSource() == configButton) {
                new SettingsDialog (pres);
            }
        } catch (QTException qte) {
            qte.printStackTrace();
        }
    }

This is pretty straightforward handling of the start/stop and config buttons. If the clicked button is start/stop, the config GUI has to call start() or stop() on the Presentation, start or stop tasking (the periodic callbacks to this class' task() method), set the broadcasting flag for the benefit of the next button click, and change the label on the button. If the user clicked on configure, it launches a new SettingsDialog for the Presentation.


	public synchronized final void task() throws QTException {
		pres.idle(null);
	}

}

This final method implements the task() method inherited from Tasking and periodically called after startTasking() is called by the start button handler. By simply calling Presentation.idle(), it gives the presentation time to acquire current data from the capture devices, encode it, and stream it out.

Running the Streaming Client

The simplest way to get a client to see the broadcast is to use QuickTime Player to open the same SDP file that the server used to create the Presentation. This will invoke the SDP Importer to connect to the streams and start parsing their contents. Note that the client cannot be on the same machine as the server, apparently since the server will have occupied the ports used for the presentation, denying the client the ability to bind to those ports. Figure 4 shows what streaming from my desktop looks like (when I'm playing with my Macross and Escaflowne toys, that is).


Figure 4. QuickTime streaming client

If you're using QuickTime Player, you can use its Get Info command to reveal the two streams and their formats. In Figure 5, you can see there are two streams: an uncompressed 44.1kHz audio stream, and an H.263 video stream.


Figure 5. Client info window

Conclusion

As I said, kicking off a stream with QuickTime for Java is a lot easier than it has been assumed to be. The simplest case, broadcasting from capture devices, takes less than 150 lines of code. By far, the hardest part is understanding the SDP file, which proved extremely finicky and whose documentation presupposes a lot of knowledge that application-level programmers may not have. It's also unfortunate that QTJ doesn't provide a preview component anymore, but it may do so in the future, and a little bit of GWorld/QuickDraw hacking may produce such a component in the future.

This article only covered how to set up a broadcast for live-capture data. Other available Sourcers, such as those to broadcast QuickTime files from disk or arbitrary content, will be discussed in a future installment of this series.

Thanks to various members of quicktime-api for help deciphering SDP files for this article.

Resources

Chris Adamson is an author, editor, and developer specializing in iPhone and Mac.


Return to ONJava.com