Exporting to a list of known formats is limiting—if the end user has installed new movie exporters, either from third parties or via an update to QuickTime itself, a program that uses a canned list of exporters won’t be able to pick them up. Fortunately, QuickTime provides a means of querying for installed components of a given type. You can use this strategy to offer a list of all available exporters.
The AdvancedMovieExport
eliminates the three
canned entries in the choices
array that were used
by SimpleMovieExport
(shown in Example 4-2) and instead builds the array through a
process of discovery; this code would replace the short
“build choices” block in the
constructor for SimpleMovieExport
but needs to go
inside the try-catch, because it makes calls that can throw
QTException
:
Vector choices = new Vector( ); ComponentIdentifier ci = null; ComponentDescription cd = new ComponentDescription(StdQTConstants.movieExportType); while ( (ci = ComponentIdentifier.find(ci, cd)) != null) { // check to see that the movie can be exported // with this component (this throws some obnoxious // exceptions, maybe a bit expensive?) try { MovieExporter exporter = new MovieExporter (ci); if (exporter.validate (movie, null)) { ExportChoice choice = new ExportChoice (ci.getInfo( ).getName( ), ci); choices.addElement(choice); } } catch (StdQTException expE) { System.out.println ("** can't validate " + ci.getInfo( ).getName( ) + " **");
// expE.printStackTrace( ); } // ow! }
When run, the list of supported exporters is surprisingly large, as seen in Figure 4-5. In this case, a “normal” movie, consisting of a video track and an audio track, is being exported, meaning that any audio-only format (Wave, AIFF, etc.) or audio/video format (QuickTime, AVI, MPEG-4, etc.) will work.
Note
Hinted Movie, the format selected in Figure 4-5, is a QuickTime movie with “hints” to optimize streaming.
You also should take note of the discovered exporters that cannot export the movie. These are logged to standard out:
run-ch04-advancedmovieexport: [java] ** can't validate BMP ** [java] ** can't validate Standard MIDI ** [java] ** can't validate Picture ** [java] ** can't validate Text ** [java] ** can't validate QuickTime TeXML ** [java] ** can't validate QuickTime Media Link **
These fail because the source movie doesn’t contain tracks that can be exported to these formats. With a source movie with different kinds of tracks, some of these would succeed and others would fail.
The process of discovering components by subtype is rather peculiar.
It hinges on making repeated calls to a
“find” method, passing in the last
matching component. Doing this requires a
ComponentDescription
, used as a template to match against, and
a ComponentIdentifier
, which refers to a specific
component (though not a specific instance of that component). To find
movie exporters, initialize a ComponentDescription
template with the constant movieExporterType
.
The static ComponentIdentifier.find()
method finds matching
components, but instead of offering an array or other collection of
matches, it requires you to repeatedly pass in the
ComponentDescription
template, along with the
previous ComponentIdentifier
found by the method.
For the first iteration, this will be null
. The
find( )
call returns a
ComponentIdentifier
, which you pass to the
MovieExporter
constructor to create a new
exporter. When find( )
returns
null
, there are no more matches.
The matched
ComponentIdentifier
provides information
about itself via the getInfo( )
method. This
returns another ComponentDescription
object,
different from the one used as a template. You can use this to get
type and subtype information (as FOUR_CHAR_CODE
int
s, of course), a name, an information
String
, a manufacturer code, etc.
Finding a MovieExporter
is no guarantee that it
actually will work. You can call validate( )
, as
this example does, to check that the instantiated exporter can do an
export from the given movie. In this example, if validate throws an
exception, it’s logged to standard out and the
exporter is not added to the JComboBox
.
...setting the
export parameters programmatically, instead of using the export
dialog every time? This is possible, although it will require using
the export dialog at least once in development. A configured
MovieExporter
can return its configured state in
the form of an AtomContainer
object, by way of the
getExportSettingsFromAtomContainer()
method. This object can be passed to an
exporter via the setExportSettingsFromAtomContainer()
method.
Note
"Atoms” are a low-level data structure that do almost all of QuickTime’s heavy lifting. Application-level code uses them only for really advanced stuff (see Chapter 9).
Within a single running application, this is pretty straightforward.
To persist between sessions, you must save off the native structure
by calling getBytes( )
on the
AtomContainer
and then persist it to disk,
database, etc. To recreate the settings in the future, read the bytes
into a byte array, create a QTHandle
from the
array, and then pass that to AtomContainer.fromQTHandle( )
to create the
AtomContainer
.
QuickTime 6.3 introduced a new API for setting exporters programmatically, but as of this writing, it has not been exposed via QTJ method calls.
Also, if I specify type and subtype, will I always get one match? No, in some cases, you’ll get multiple matching components, and you might need to use other criteria to pick which one to use. In a rather infamous case pointed out by one of my tech reviewers:
Sometimes you get more than one exporter with the same subtype and need to use the “manufacturer” code to distinguish them. This applies particularly to AIFF exporters—the first exporter you find of that type only exports MIDI. To export an arbitrary QT audio file to AIFF you need to explicitly iterate and pick the second one!
Get QuickTime for Java: A Developer's Notebook now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.