Monitoring the Load State

Calling any NewMovieFrom... function initiates a series of events that you can monitor by making calls to GetMovieLoadState. The initial load state is always kMovieLoadStateLoading, at least briefly. Eventually the load state changes to either kMovieLoadStateComplete or kMovieLoadStateError. There are often additional intermediate load states.

All NewMovieFrom... functions support asynchronous movie loading. This is controlled by passing a flag when calling the function. When asynchronous loading is enabled, the NewMovieFrom... function returns almost immediately, so that your application is not blocked while QuickTime locates the movie data source, resolves its media sample data references, downloads any necessary files, or performs any necessary import. You need to monitor the progress of the movie loading process by checking the load state periodically by calling GetMovieLoadState.

All NewMovieFrom... functions, including NewMovieFromProperties, load asynchronously only if the newMovieAsyncOK flag is passed (when calling NewMovieFromProperties, this flag is passed in the NewMovieProperties property).

If asynchronous loading is not used, any NewMovieFrom... function blocks until the movie finds and loads all referenced data or the process fails, and the load state resolves to either kMovieLoadStateComplete or kMovieLoadStateError.

The possible load states during asynchronous movie loading are these:

The Download Sequence

The exact sequence of events depends on whether the movie source is local or must be downloaded, whether it is self-contained or references external data, and whether it is a QuickTime movie or a source for which QuickTime uses an importer.

Local Movies

The simplest example of load states is a self-contained movie file on a local disk. QuickTime locates the file, finds and copies the movie data structure, then determines the location of the media sample data for each track. In this case, the data are all stored in the local movie file, so the movie is immediately complete and ready to play. The load state goes directly from kMovieLoadStateLoading to kMovieLoadStateComplete.

In the case of a movie file with external media data, QuickTime locates the file, finds and copies the movie data structure, then attempts to locate the necessary data files, streams, or storage containers.

Until all of the external data references are resolved, the load state remains kMovieLoadStateLoading. If all data references cannot be resolved, an error is returned, but the movie is still created in memory. It is not playable, however.

When QuickTime has resolved all the references to external data, the movie is ready to play. If all the data is in local files, the load state goes immediately to kMovieLoadStateComplete. If the data must download or stream over a network, the load state goes to kMovieLoadStatePlayable; at some point, depending on the network transmission speed and the movie’s data rate, enough of the data downloads so that QuickTime expect to be able to play the movie without interruption (at the current transmission rate, the rest of the data will arrive before it is needed) and the load state changes to kMovieLoadStatePlaythroughOK.

When all the media samples referenced in the movie are available, the movie load state is kMovieLoadStateComplete. If the movie references data in a live stream, data may still be coming in, but all streams are online.

Movie Files on the Internet

A more complex example is a movie file downloaded over the Internet. QuickTime begins downloading the file and looking for the movie data structure (kMovieLoadStateLoading). Once the movie data structure has loaded, QuickTime can resolve any references to external media sample data. Once all data references are resolved, the movie state changes to kMovieLoadStatePlayable. As more media data arrives, the state changes to kMovieLoadStatePlaythroughOK, and finally to kMovieLoadStateComplete when all the data is available.

Note, however, that none of this takes place until the movie data structure has been downloaded. If the movie data structure is stored at the beginning of the file, as is normal, the sequence is as described. This is called faststart, or progressivedownload, because the movie is almost instantly playable and can be played through while it is still downloading. If the available bandwidth is greater than the movie bandwidth requirements, the movie state goes almost immediately to kMovieLoadStatePlaythroughOK.

But if the movie data structure is at the end of the file, the state remains kMovieLoadStateLoading until the entire file downloads. Not until then can QuickTime determine what types of media are used in the movie, where the movie sample data is located, or even if the file is actually a QuickTime movie file. If the movie file has no external data dependencies, the state goes suddenly from kMovieLoadStateLoading to kMovieLoadStateComplete.

Non-Movie Data Sources

In addition to QuickTime movie files, all of the NewMovieFrom... functions work with data sources that QuickTime can import in place, such as MP3 audio, MPEG-1 video, and so on. (For a complete list of formats that QuickTime can import in place, see File Types that QuickTime Can Open as Movies.)

If the data source does not contain a stored movie, QuickTime attempts to create a movie in memory that describes the sample data. This is typically done by searching the list of available movie import components, based on the file type, MIME type, or filename extension of the data source.

Not all movie import components support asynchronous loading; some components must have access to the entire file before they can begin providing movie data. Import components that support asynchronous loading are called idlingimporters (because the system grants them idle time after calling them). To enable movie import components to operate asynchronously, pass the newMovieIdleImportOK flag to the NewMovieFrom... function.

Once QuickTime has enough information to create an appropriate movie data structure for the data source, the movie state changes from kMovieLoadStateLoading to kMovieLoadStateComplete for a local file or a synchronous importer, or sequentially to kMovieLoadStatePlayable, kMovieLoadStatePlaythroughOK, then kMovieLoadStateComplete for a file that is downloading over the Internet through an idling importer.

If no importer can be found for the data source you specify, the movie load state changes to kMovieLoadStateError.

Example of Getting a Movie and Monitoring the Load State

Listing 1-5 illustrates getting a movie and monitoring the load state.

Listing 4-1  Getting a movie and monitoring the load state

 
Boolean CreateAMovie (void)
{
Point where = {100,100};
Movie theMovie = nil;
short resRefNum = 0;
short resId = movieInDataForkResID;
StringPtr fileName = QTUtils_ConvertCToPascalString ("MovieFile.mov");
Boolean isSelected = false;
Boolean isReplaceing = false;
OSErr err = noErr;
const unsigned char url[] =
"rtsp://a.movieserver.net/path/amovie.mov";  // create a dummy url for testing
HandledataRef = nil;
longloadState;
ComponentResult result;
Track firstProblemTrack;
 
// Create a data reference which we will use to instantiate our movie.
// In this case, we'll construct a URL data reference. The URL data reference
// is simply a handle whose data is a URL describing a movie.
// We'll build the data reference manually, just for the experience.
 
dataRef = NewHandleClear(StrLength(url) + 1);
CheckError(MemError(), "NewHandleClear error");
BlockMoveData(url, *dataRef, StrLength(url) + 1);
 
// Instantiate the movie file using NewMovieFromDataRef and a URL data reference.
// We make sure to pass the newMovieAsyncOK flag to enable
// us to query the state of the movie as it loads via the
// GetMovieLoadState function.
 
err = NewMovieFromDataRef(&theMovie,
newMovieActive | newMovieAsyncOK,
nil,
dataRef,
URLDataHandlerSubType);
 
// Handle asynchronous movie loading here. We use the
// GetMovieLoadState function to determine the load state
// of the movie.
do
{
long newLoadState;
 
// Get new load state to see if there was a change in
// state.
newLoadState = GetMovieLoadState(theMovie);
if (newLoadState != loadState)
{
loadState = newLoadState;
if (loadState < 0)
{
// failed to load the movie
// drop out of this loop and report an error
}
 
// movie loading--we need to keep tasking the movie so it gets
// time to load
MoviesTask(theMovie, 0);
 
if (loadState < kMovieLoadStatePlayable)
{
// movie still loading...
}
 
if (loadState < kMovieLoadStatePlaythroughOK)
{
// movie is playable but the media are still downloading...
}
 
if (loadState < kMovieLoadStateComplete)
{
// movie playable --
// some media still downloading, but all media is
// expected  to arrive before it is needed.
// we could start playing the movie now...
}
 
if (loadState >= kMovieLoadStateComplete)
{
// all media data is available
// drop out of this loop
// and play the movie
}
}
}
while ((loadState > kMovieLoadStateError) && (loadState < kMovieLoadStateComplete));
 
CheckError(err, "NewMovieFromDataRef error");
 
// dispose of our data reference handle since it is no longer needed
DisposeHandle(dataRef);
 
result = GetMovieStatus (theMovie, &firstProblemTrack);
// if GetMovieLoadState from above returned kMovieLoadStateError, and
// GetMovieStatus returns nil for the firstProblemTrack parameter we
// know an error occurred
if ((loadState == kMovieLoadStateError) && (firstProblemTrack == nil))
{
CheckError(-1, "NewMovieFromDataRef error");
}
 
// create a default FSSpec
FSMakeFSSpec(0, 0L, fileName, &fsspec);
 
bail:
free(fileName);
return(err);
}