Home > Blogs > Ask Big Nerd Ranch: Detecting That QuickTime Has Exhausted Its Stream

Ask Big Nerd Ranch: Detecting That QuickTime Has Exhausted Its Stream

Jeremy W. Sherman answers a question about QuickTime.

Q. If a QTMovie is loading audio from the web and the stream stops, how can I detect whether the QTMovie stopped?

A. Suppose you have a QTMovieView playing a QTMovie. The QTMovie is loading media from the Web. Now, suppose your network connection drops.

What happens next depends on the structure of the movie file itself. The movie file might contain the data itself, or it might reference a broadcast stream.

Say the movie file actually contains the data. If it finished downloading before you started playback, then the status of the network connection no longer affects playback. If it had not finished downloading, but it was already playing, then the movie supports “progressive download.” This means that the data it needs for playback at a given time is grouped in the file so that you can begin playing the movie without having downloaded the entire file. Because the data is stored locally, when the connection drops, the user will be able to play the portion of the movie that has already downloaded as many times as they wish.

You can detect when playback halts by registering for notifications named QTMovieRateDidChangeNotification sent by the QTMovie in question (see Listing 1); when the rate drops to 0, playback has halted. This could also be due to the user pausing playback. To check whether you have hit the end of the downloaded portion of the movie, you need to call the classic QuickTime function GetMaxLoadedTimeInMovie() and compare that against the current movie time (Listing 2). Unfortunately, the QuickTime API is unavailable to 64-bit applications.

Listing 1 – Registering for QTMovieRateDidChangeNotification
- (void)monitorMovie:(QTMovie *)movie {
   [[NSNotificationCenter defaultCenter]
    addObserver:self selector:@selector(movieRateChanged:)
    name:QTMovieRateDidChangeNotification object:movie];

Listing 2 – Checking for a Halted Stream
- (void)movieRateChanged:(NSNotification *)note {
   QTMovie *const movie = [note object];
   const BOOL playbackPaused = (0 == [movie rate]);
   if (!playbackPaused) return;
   const QTTime currTime = [movie currentTime];
   const BOOL
   atEnd = (QTTimeCompare(currTime, [movie duration])
            == NSOrderedSame);
   if (atEnd) {
      NSLog(@"%s: Hit end of movie.", __func__);
   Movie m = [movie quickTimeMovie];
   TimeValue tv;
   const OSStatus err = GetMaxLoadedTimeInMovie(m, &tv);
   if (noErr != err) {
      NSLog(@"%s: *** GetMaxLoadedTimeInMovie: %s - %s",
            __func__, GetMacOSStatusErrorString(err),
   const TimeScale ts = GetMovieTimeScale(m);
   const QTTime loadTime = QTMakeTime(tv, ts);
   const BOOL
   atLoad = (QTTimeCompare(currTime,loadTime)
             == NSOrderedSame);
   if (atLoad) NSLog(@"%s: Ran out of data!", __func__);
   else NSLog(@"%s: User paused playback.", __func__);

The outlook is not so rosy for streaming media. Even QuickTime itself seems utterly unaware that the stream has stopped: when the network connection drops, the QTMovieView’s scrubber keeps advancing without any change in rate, though any audio has stopped and any video frozen. It’s only when you try to seek to a time – pause then play, or move the scrubber – that QuickTime signals an error. It seems, then, that the MovieController and everything built on top of it, including QTMovieView, are utterly unaware that the stream has come to an abrupt end.

Suppose you wanted to force QuickTime to seek regularly to provoke an error message as soon as the connection drops. You wouldn’t want to disturb playback, so you’d want to do an in-place seek: pause, then unpause. Unfortunately, restarting playback of a stream can take a few seconds, so this approach is unworkable.

You would think that something within QuickTime must be aware of the state of the network data stream. You must be right, but QuickTime makes it impossible to get at this information. All attempts to cadge any information about the state of the actual streaming data from the guts of QuickTime fail.

Because the movie file is simply a stream reference, as soon as the file has been downloaded, the QuickTime reports the movie has fully loaded. Once QuickTime has received the movie duration, getting the movie’s maximum loaded time always reports the entire movie as loaded, even when it plainly has not been.

Interrogating the movie’s data handler is likewise useless: it does not handle the streaming data itself, so the handler reports a data rate of 0 bytes per second, says it has loaded the entire file, and further declares itself a non-streaming handler that never streams.

The QuickTime Streaming functions look promising: a QTSPresentation wraps one or more QTSStreams, and each stream reports several statistics, including the inbound data rate. When this hits zero, you have run out of streaming data. Unfortunately, the API appears suitable only for broadcasting. All attempts to create a streaming presentation using QTSNewPresentationFromDataRef() and QTSNewPresentationFromFile() with QTSPresParams flags of kQTSAutoModeFlag | kQTSReceiveMediaFlag ended with an “Unsupported Data Type” error.

Want an answer to your Mac programming question? Submit your question to www.informit.com/askbnr.