cocoasynchronizationquicktimeqtkit

Swapping QTMovieView on an NSWindow causes flicker


What I would like to do is this:

This is what i've done:

my full code is at http://codepad.org/gDsJLPAy

It all works, however when I switch videos, I get a single frame flash of the old frame of the video being switched to. I.e. if I run the app for 50 frames and switch to SlaveVideo2, I catch a glimpse of SlaveVideo2 frame 0, and then it immediately jumps to SlaveVideo2 frame 50 and carries on as usual. If, after 20 frames I switch back to SlaveVideo1 I catch a glimpse of SlaveVideo1 frame 50, and then it jumps to SlaveVideo1 frame 70 and carries on as usual.

I'm going crazy trying to figure this out. I've tried all kinds of refresh, display methods but to no avail. Any ideas on how I can fix this?

P.S. You can see in the timerFireMethod if I update all slave movies all the time (instead of just the active one), I don't get the flicker. Obviously this isn't a solution since I'm constantly seeking tons of videos which is way too heavy and unnecessary!

P.P.S. I've tried recreating the setup in Quartz Composer and I get the exact same problem.

I'm on 10.7.3


Solution

  • For the sake of the archives I'll post my conclusions here.

    I couldn't find a direct solution to this problem. If the slave movie is paused, when I make its view active - even if I start playing the movie slightly earlier - upon becoming visible it first shows the old frame. The workaround solution I found was to keep the movie playing at all times (which sucks).

    However I found this https://developer.apple.com/library/mac/#documentation/QuickTime/RM/MovieInternals/MTTimeSpace/B-Chapter/2MovieTimeandSpace.html

    and by calling GetMovieTimeBase on the master Movie, and applying SetTimeBaseMasterTimeBase to the slave movie, it allows the two movies to play in sync in the back end, without you having to manually keep them in sync. So the whole sync code can be reduced to:

    -(void)setLayerIndex:(int)i {
        // make sure target index is within bounds
        if(i<0) i=[[movies objectAtIndex:1] count]-1;
        else if(i>[[movies objectAtIndex:1] count]-1) i=0;
    
        // pause all slave views
        [[viewers objectAtIndex:1] makeObjectsPerformSelector:@selector(pause:) withObject:self];
    
        // save current index
        currentLayerIndex = i;
    
        // sync frame of new slave movie
        [[self currentSlaveMovie] setCurrentTime:[masterMovie currentTime]];
    
        // set windows view to QTView
        [[windows objectAtIndex:1] setContentView:[self currentSlaveView]];
    
        // set timebase of slave to timebase of master
        TimeBase masterTimeBase = GetMovieTimeBase([masterMovie quickTimeMovie]);
        TimeValue masterTimeScale = GetMovieTimeScale([masterMovie quickTimeMovie]);
        TimeRecord slaveZero;
        TimeValue slaveZeroTV = GetTimeBaseStartTime(masterTimeBase, masterTimeScale, &slaveZero); 
        SetMovieMasterTimeBase([[self currentSlaveMovie] quickTimeMovie], masterTimeBase, &slaveZero);    
    
        // play master movie
        [masterViewer play:self];
    
        // play slave movie
        [[self currentSlaveView] play:self];
    

    }