QTKit how to record a movie to a file for a specific duration

154 views Asked by At

I'm having trouble with what I think should be an easy task.

I'm trying to record video from any attached webcam to a file for a specific duration. But the duration that actually gets recorded is always about 3/4 of a second off. E.g. 5 secs gets recorded as 4.28

Following some advice from Googling and stackoverflow-ing, i've seen that a simple way to do this is to use setMaximumRecordedDuration on the movie output file, and then stop capturing in the delegate captureOutput method.

For instance for capturing 5 secs of video, I'm using the following code right now;

    // set maximum duration (e.g. 5 secs at 30000 timescale) ??
    [mCaptureMovieFileOutput recordToOutputFileURL:[NSURL fileURLWithPath:path]];
    [mCaptureMovieFileOutput setMaximumRecordedDuration: QTMakeTime(5*30000, 30000)];
    [mCaptureSession startRunning];

    // start a run loop (for how long?)
    verbose("(recording for %.2lf seconds)\n", [recordSeconds doubleValue]);
    NSDate *now = [[NSDate alloc] init];
    [[NSRunLoop currentRunLoop] runUntilDate:[now dateByAddingTimeInterval: [recordSeconds doubleValue]]];
    verbose("(recording period ended)\n");

Then in my captureOutput delegate method I have;

- (BOOL)captureOutput:(QTCaptureFileOutput *)captureOutput shouldChangeOutputFileAtURL:(NSURL *)outputFileURL forConnections:(NSArray *)connections dueToError:(NSError *)error {
    QTTime durationRecorded = [mCaptureMovieFileOutput recordedDuration];
    NSString *outputDuration = QTStringFromTime(durationRecorded);
    verbose("(capture output reached %s !)\n", [outputDuration UTF8String]);

    [mCaptureMovieFileOutput recordToOutputFileURL:nil];

    return YES;
}

The full code for this is here; https://github.com/matthutchinson/videosnap/blob/master/videosnap/videosnap.m

This code is part of a new open source command line app i'm working on VideoSnap. You can see the full source code, download and open it in xCode from GitHub

3 questions;

  • how can I set setMaximumRecordedDuration to make this work for 5 seconds
  • the run loop I have is a hack right now, but when I use e.g. while(recordingFlag) the capturing never starts
  • is this the best way to do this? should I count frames somehow or something else?

Below is the pasted output from the above code;

(no filename specified, using default)
(no device specified, using default)
(options before recording)
  delay:    0.00s
  record:   10.00s
  filename: movie.mov
  device:   Built-in iSight
(no delay)
(starting device...)
(recording for 10.00 seconds)
(recorded (null)  - frameCount 0!)
(recorded (null)  - frameCount 1!)
(recorded (null)  - frameCount 2!)
(recorded (null)  - frameCount 3!)
(recorded (null)  - frameCount 4!)
(recorded (null)  - frameCount 5!)
(recorded (null)  - frameCount 6!)
(recorded (null)  - frameCount 7!)
(recorded (null)  - frameCount 8!)
(recorded (null)  - frameCount 9!)
(recorded (null)  - frameCount 10!)
(recorded (null)  - frameCount 11!)
(recorded (null)  - frameCount 12!)
(recorded (null)  - frameCount 13!)
(recorded (null)  - frameCount 14!)
(recorded (null)  - frameCount 15!)
(recorded (null)  - frameCount 16!)
(recorded (null)  - frameCount 17!)
(recorded (null)  - frameCount 18!)
(recorded (null)  - frameCount 19!)
(recorded (null)  - frameCount 20!)
(recorded (null)  - frameCount 21!)
(recorded (null)  - frameCount 22!)
(recorded (null)  - frameCount 23!)
(recorded 0:00:00:00.00/1  - frameCount 24!)
(recorded 0:00:00:00.00/1  - frameCount 25!)
(recorded 0:00:00:00.00/1  - frameCount 26!)
(recorded 0:00:00:00.18000/30000  - frameCount 27!)
(recorded 0:00:00:00.18000/30000  - frameCount 28!)
(recorded 0:00:00:00.18000/30000  - frameCount 29!)
(recorded 0:00:00:00.20000/30000  - frameCount 30!)
(recorded 0:00:00:00.20000/30000  - frameCount 31!)
(recorded 0:00:00:00.20000/30000  - frameCount 32!)
(recorded 0:00:00:00.20000/30000  - frameCount 33!)
(recorded 0:00:00:00.22000/30000  - frameCount 34!)
(recorded 0:00:00:00.22000/30000  - frameCount 35!)
(recorded 0:00:00:00.22000/30000  - frameCount 36!)
(recorded 0:00:00:00.22000/30000  - frameCount 37!)
(recorded 0:00:00:00.24000/30000  - frameCount 38!)
(recorded 0:00:00:00.24000/30000  - frameCount 39!)
(recorded 0:00:00:00.24000/30000  - frameCount 40!)
(recorded 0:00:00:00.24000/30000  - frameCount 41!)
(recorded 0:00:00:00.26000/30000  - frameCount 42!)
(recorded 0:00:00:00.26000/30000  - frameCount 43!)
(recorded 0:00:00:00.26000/30000  - frameCount 44!)
(recorded 0:00:00:00.26000/30000  - frameCount 45!)
(recorded 0:00:00:00.28000/30000  - frameCount 46!)
(recorded 0:00:00:00.28000/30000  - frameCount 47!)
(recorded 0:00:00:00.28000/30000  - frameCount 48!)
(recorded 0:00:00:00.28000/30000  - frameCount 49!)
(recorded 0:00:00:01.00/30000  - frameCount 50!)
(recorded 0:00:00:01.00/30000  - frameCount 51!)
(recorded 0:00:00:01.00/30000  - frameCount 52!)
(recorded 0:00:00:01.00/30000  - frameCount 53!)
(recorded 0:00:00:01.00/30000  - frameCount 54!)
(recorded 0:00:00:01.00/30000  - frameCount 55!)
(recorded 0:00:00:01.00/30000  - frameCount 56!)
(recorded 0:00:00:01.4000/30000  - frameCount 57!)
(recorded 0:00:00:01.4000/30000  - frameCount 58!)
(recorded 0:00:00:01.4000/30000  - frameCount 59!)
(recorded 0:00:00:01.4000/30000  - frameCount 60!)
(recorded 0:00:00:01.6000/30000  - frameCount 61!)
(recorded 0:00:00:01.6000/30000  - frameCount 62!)
(recorded 0:00:00:01.6000/30000  - frameCount 63!)
(recorded 0:00:00:01.6000/30000  - frameCount 64!)
(recorded 0:00:00:01.8000/30000  - frameCount 65!)
(recorded 0:00:00:01.8000/30000  - frameCount 66!)
(recorded 0:00:00:01.8000/30000  - frameCount 67!)
(recorded 0:00:00:01.8000/30000  - frameCount 68!)
(recorded 0:00:00:01.10000/30000  - frameCount 69!)
(recorded 0:00:00:01.10000/30000  - frameCount 70!)
(recorded 0:00:00:01.10000/30000  - frameCount 71!)
(recorded 0:00:00:01.10000/30000  - frameCount 72!)
(recorded 0:00:00:01.12000/30000  - frameCount 73!)
(recorded 0:00:00:01.12000/30000  - frameCount 74!)
(recorded 0:00:00:01.12000/30000  - frameCount 75!)
(recorded 0:00:00:01.12000/30000  - frameCount 76!)
(recorded 0:00:00:01.14000/30000  - frameCount 77!)
(recorded 0:00:00:01.14000/30000  - frameCount 78!)
(recorded 0:00:00:01.14000/30000  - frameCount 79!)
(recorded 0:00:00:01.14000/30000  - frameCount 80!)
(recorded 0:00:00:01.16000/30000  - frameCount 81!)
(recorded 0:00:00:01.16000/30000  - frameCount 82!)
(recorded 0:00:00:01.16000/30000  - frameCount 83!)
(recorded 0:00:00:01.16000/30000  - frameCount 84!)
(recorded 0:00:00:01.16000/30000  - frameCount 85!)
(recorded 0:00:00:01.16000/30000  - frameCount 86!)
(recorded 0:00:00:01.16000/30000  - frameCount 87!)
(recorded 0:00:00:01.20000/30000  - frameCount 88!)
(recorded 0:00:00:01.20000/30000  - frameCount 89!)
(recorded 0:00:00:01.20000/30000  - frameCount 90!)
(recorded 0:00:00:01.20000/30000  - frameCount 91!)
(recorded 0:00:00:01.22000/30000  - frameCount 92!)
(recorded 0:00:00:01.22000/30000  - frameCount 93!)
(recorded 0:00:00:01.22000/30000  - frameCount 94!)
(recorded 0:00:00:01.22000/30000  - frameCount 95!)
(recorded 0:00:00:01.24000/30000  - frameCount 96!)
(recorded 0:00:00:01.24000/30000  - frameCount 97!)
(recorded 0:00:00:01.24000/30000  - frameCount 98!)
(recorded 0:00:00:01.24000/30000  - frameCount 99!)
(recorded 0:00:00:01.26000/30000  - frameCount 100!)
(recorded 0:00:00:01.26000/30000  - frameCount 101!)
(recorded 0:00:00:01.26000/30000  - frameCount 102!)
(recorded 0:00:00:01.26000/30000  - frameCount 103!)
(recorded 0:00:00:01.28000/30000  - frameCount 104!)
(recorded 0:00:00:01.28000/30000  - frameCount 105!)
(recorded 0:00:00:01.28000/30000  - frameCount 106!)
(recorded 0:00:00:01.28000/30000  - frameCount 107!)
(recorded 0:00:00:02.00/30000  - frameCount 108!)
(recorded 0:00:00:02.00/30000  - frameCount 109!)
(recorded 0:00:00:02.00/30000  - frameCount 110!)
(recorded 0:00:00:02.00/30000  - frameCount 111!)
(recorded 0:00:00:02.2000/30000  - frameCount 112!)
(recorded 0:00:00:02.2000/30000  - frameCount 113!)
(recorded 0:00:00:02.2000/30000  - frameCount 114!)
(recorded 0:00:00:02.2000/30000  - frameCount 115!)
(recorded 0:00:00:02.2000/30000  - frameCount 116!)
(recorded 0:00:00:02.2000/30000  - frameCount 117!)
(recorded 0:00:00:02.2000/30000  - frameCount 118!)
(recorded 0:00:00:02.6000/30000  - frameCount 119!)
(recorded 0:00:00:02.6000/30000  - frameCount 120!)
(recorded 0:00:00:02.6000/30000  - frameCount 121!)
(recorded 0:00:00:02.6000/30000  - frameCount 122!)
(recorded 0:00:00:02.8000/30000  - frameCount 123!)
(recorded 0:00:00:02.8000/30000  - frameCount 124!)
(recorded 0:00:00:02.8000/30000  - frameCount 125!)
(recorded 0:00:00:02.8000/30000  - frameCount 126!)
(recorded 0:00:00:02.10000/30000  - frameCount 127!)
(recorded 0:00:00:02.10000/30000  - frameCount 128!)
(recorded 0:00:00:02.10000/30000  - frameCount 129!)
(recorded 0:00:00:02.10000/30000  - frameCount 130!)
(recorded 0:00:00:02.12000/30000  - frameCount 131!)
(recorded 0:00:00:02.12000/30000  - frameCount 132!)
(recorded 0:00:00:02.12000/30000  - frameCount 133!)
(recorded 0:00:00:02.12000/30000  - frameCount 134!)
(recorded 0:00:00:02.14000/30000  - frameCount 135!)
(recorded 0:00:00:02.14000/30000  - frameCount 136!)
(recorded 0:00:00:02.14000/30000  - frameCount 137!)
(recorded 0:00:00:02.14000/30000  - frameCount 138!)
(recorded 0:00:00:02.16000/30000  - frameCount 139!)
(recorded 0:00:00:02.16000/30000  - frameCount 140!)
(recorded 0:00:00:02.16000/30000  - frameCount 141!)
(recorded 0:00:00:02.16000/30000  - frameCount 142!)
(recorded 0:00:00:02.18000/30000  - frameCount 143!)
(recorded 0:00:00:02.18000/30000  - frameCount 144!)
(recorded 0:00:00:02.18000/30000  - frameCount 145!)
(recorded 0:00:00:02.18000/30000  - frameCount 146!)
(recorded 0:00:00:02.18000/30000  - frameCount 147!)
(recorded 0:00:00:02.18000/30000  - frameCount 148!)
(recorded 0:00:00:02.18000/30000  - frameCount 149!)
(recorded 0:00:00:02.22000/30000  - frameCount 150!)
(recorded 0:00:00:02.22000/30000  - frameCount 151!)
(recorded 0:00:00:02.22000/30000  - frameCount 152!)
(recorded 0:00:00:02.22000/30000  - frameCount 153!)
(recorded 0:00:00:02.24000/30000  - frameCount 154!)
(recorded 0:00:00:02.24000/30000  - frameCount 155!)
(recorded 0:00:00:02.24000/30000  - frameCount 156!)
(recorded 0:00:00:02.24000/30000  - frameCount 157!)
(recorded 0:00:00:02.26000/30000  - frameCount 158!)
(recorded 0:00:00:02.26000/30000  - frameCount 159!)
(recorded 0:00:00:02.26000/30000  - frameCount 160!)
(recorded 0:00:00:02.26000/30000  - frameCount 161!)
(recorded 0:00:00:02.28000/30000  - frameCount 162!)
(recorded 0:00:00:02.28000/30000  - frameCount 163!)
(recorded 0:00:00:02.28000/30000  - frameCount 164!)
(recorded 0:00:00:02.28000/30000  - frameCount 165!)
(recorded 0:00:00:03.00/30000  - frameCount 166!)
(recorded 0:00:00:03.00/30000  - frameCount 167!)
(recorded 0:00:00:03.00/30000  - frameCount 168!)
(recorded 0:00:00:03.00/30000  - frameCount 169!)
(recorded 0:00:00:03.2000/30000  - frameCount 170!)
(recorded 0:00:00:03.2000/30000  - frameCount 171!)
(recorded 0:00:00:03.2000/30000  - frameCount 172!)
(recorded 0:00:00:03.2000/30000  - frameCount 173!)
(recorded 0:00:00:03.4000/30000  - frameCount 174!)
(recorded 0:00:00:03.4000/30000  - frameCount 175!)
(recorded 0:00:00:03.4000/30000  - frameCount 176!)
(recorded 0:00:00:03.4000/30000  - frameCount 177!)
(recorded 0:00:00:03.4000/30000  - frameCount 178!)
(recorded 0:00:00:03.4000/30000  - frameCount 179!)
(recorded 0:00:00:03.4000/30000  - frameCount 180!)
(recorded 0:00:00:03.8000/30000  - frameCount 181!)
(recorded 0:00:00:03.8000/30000  - frameCount 182!)
(recorded 0:00:00:03.8000/30000  - frameCount 183!)
(recorded 0:00:00:03.8000/30000  - frameCount 184!)
(recorded 0:00:00:03.10000/30000  - frameCount 185!)
(recorded 0:00:00:03.10000/30000  - frameCount 186!)
(recorded 0:00:00:03.10000/30000  - frameCount 187!)
(recorded 0:00:00:03.10000/30000  - frameCount 188!)
(recorded 0:00:00:03.12000/30000  - frameCount 189!)
(recorded 0:00:00:03.12000/30000  - frameCount 190!)
(recorded 0:00:00:03.12000/30000  - frameCount 191!)
(recorded 0:00:00:03.12000/30000  - frameCount 192!)
(recorded 0:00:00:03.14000/30000  - frameCount 193!)
(recorded 0:00:00:03.14000/30000  - frameCount 194!)
(recorded 0:00:00:03.14000/30000  - frameCount 195!)
(recorded 0:00:00:03.14000/30000  - frameCount 196!)
(recorded 0:00:00:03.16000/30000  - frameCount 197!)
(recorded 0:00:00:03.16000/30000  - frameCount 198!)
(recorded 0:00:00:03.16000/30000  - frameCount 199!)
(recorded 0:00:00:03.16000/30000  - frameCount 200!)
(recorded 0:00:00:03.18000/30000  - frameCount 201!)
(recorded 0:00:00:03.18000/30000  - frameCount 202!)
(recorded 0:00:00:03.18000/30000  - frameCount 203!)
(recorded 0:00:00:03.18000/30000  - frameCount 204!)
(recorded 0:00:00:03.20000/30000  - frameCount 205!)
(recorded 0:00:00:03.20000/30000  - frameCount 206!)
(recorded 0:00:00:03.20000/30000  - frameCount 207!)
(recorded 0:00:00:03.20000/30000  - frameCount 208!)
(recorded 0:00:00:03.20000/30000  - frameCount 209!)
(recorded 0:00:00:03.20000/30000  - frameCount 210!)
(recorded 0:00:00:03.20000/30000  - frameCount 211!)
(recorded 0:00:00:03.24000/30000  - frameCount 212!)
(recorded 0:00:00:03.24000/30000  - frameCount 213!)
(recorded 0:00:00:03.24000/30000  - frameCount 214!)
(recorded 0:00:00:03.24000/30000  - frameCount 215!)
(recorded 0:00:00:03.26000/30000  - frameCount 216!)
(recorded 0:00:00:03.26000/30000  - frameCount 217!)
(recorded 0:00:00:03.26000/30000  - frameCount 218!)
(recorded 0:00:00:03.26000/30000  - frameCount 219!)
(recorded 0:00:00:03.28000/30000  - frameCount 220!)
(recorded 0:00:00:03.28000/30000  - frameCount 221!)
(recorded 0:00:00:03.28000/30000  - frameCount 222!)
(recorded 0:00:00:03.28000/30000  - frameCount 223!)
(recorded 0:00:00:04.00/30000  - frameCount 224!)
(recorded 0:00:00:04.00/30000  - frameCount 225!)
(recorded 0:00:00:04.00/30000  - frameCount 226!)
(recorded 0:00:00:04.00/30000  - frameCount 227!)
(recorded 0:00:00:04.2000/30000  - frameCount 228!)
(recorded 0:00:00:04.2000/30000  - frameCount 229!)
(recorded 0:00:00:04.2000/30000  - frameCount 230!)
(recorded 0:00:00:04.2000/30000  - frameCount 231!)
(recorded 0:00:00:04.4000/30000  - frameCount 232!)
(recorded 0:00:00:04.4000/30000  - frameCount 233!)
(recorded 0:00:00:04.4000/30000  - frameCount 234!)
(recorded 0:00:00:04.4000/30000  - frameCount 235!)
(recorded 0:00:00:04.6000/30000  - frameCount 236!)
(recorded 0:00:00:04.6000/30000  - frameCount 237!)
(recorded 0:00:00:04.6000/30000  - frameCount 238!)
(recorded 0:00:00:04.6000/30000  - frameCount 239!)
(recorded 0:00:00:04.6000/30000  - frameCount 240!)
(recorded 0:00:00:04.6000/30000  - frameCount 241!)
(recorded 0:00:00:04.6000/30000  - frameCount 242!)
(recorded 0:00:00:04.10000/30000  - frameCount 243!)
(recorded 0:00:00:04.10000/30000  - frameCount 244!)
(recorded 0:00:00:04.10000/30000  - frameCount 245!)
(recorded 0:00:00:04.10000/30000  - frameCount 246!)
(recorded 0:00:00:04.12000/30000  - frameCount 247!)
(recorded 0:00:00:04.12000/30000  - frameCount 248!)
(recorded 0:00:00:04.12000/30000  - frameCount 249!)
(recorded 0:00:00:04.12000/30000  - frameCount 250!)
(recorded 0:00:00:04.14000/30000  - frameCount 251!)
(recorded 0:00:00:04.14000/30000  - frameCount 252!)
(recorded 0:00:00:04.14000/30000  - frameCount 253!)
(recorded 0:00:00:04.14000/30000  - frameCount 254!)
(recorded 0:00:00:04.16000/30000  - frameCount 255!)
(recorded 0:00:00:04.16000/30000  - frameCount 256!)
(recorded 0:00:00:04.16000/30000  - frameCount 257!)
(recorded 0:00:00:04.16000/30000  - frameCount 258!)
(recorded 0:00:00:04.18000/30000  - frameCount 259!)
(recorded 0:00:00:04.18000/30000  - frameCount 260!)
(recorded 0:00:00:04.18000/30000  - frameCount 261!)
(recorded 0:00:00:04.18000/30000  - frameCount 262!)
(recorded 0:00:00:04.20000/30000  - frameCount 263!)
(recorded 0:00:00:04.20000/30000  - frameCount 264!)
(recorded 0:00:00:04.20000/30000  - frameCount 265!)
(recorded 0:00:00:04.20000/30000  - frameCount 266!)
(recorded 0:00:00:04.22000/30000  - frameCount 267!)
(recorded 0:00:00:04.22000/30000  - frameCount 268!)
(recorded 0:00:00:04.22000/30000  - frameCount 269!)
(recorded 0:00:00:04.22000/30000  - frameCount 270!)
(recorded 0:00:00:04.22000/30000  - frameCount 271!)
(recorded 0:00:00:04.22000/30000  - frameCount 272!)
(recorded 0:00:00:04.22000/30000  - frameCount 273!)
(recorded 0:00:00:04.26000/30000  - frameCount 274!)
(recorded 0:00:00:04.26000/30000  - frameCount 275!)
(recorded 0:00:00:04.26000/30000  - frameCount 276!)
(recorded 0:00:00:04.26000/30000  - frameCount 277!)
(recorded 0:00:00:04.28000/30000  - frameCount 278!)
(recorded 0:00:00:04.28000/30000  - frameCount 279!)
(recorded 0:00:00:04.28000/30000  - frameCount 280!)
(recorded 0:00:00:04.28000/30000  - frameCount 281!)
(capture output reached 0:00:00:04.28000/30000 !)
(recorded 0:00:00:05.00/30000  - frameCount 282!)
(recorded 0:00:00:05.00/30000  - frameCount 283!)
(recorded 0:00:00:05.00/30000  - frameCount 284!)
(recorded 0:00:00:05.00/30000  - frameCount 285!)
(finished writing to movie file duration was 0:00:00:05.00/30000 !)
(stopping session)
(recording period ended)
DONE! movie.mov
1

There are 1 answers

0
Matt On BEST ANSWER

So I basically figured it out (with some help from a another iOS/Obj-C developer);

There were 2 problems with my code;

  • I needed a proper run loop (outside the main thread) to be running so that I can start my capture session with the camera and not exit right away (or use a while loop). To do this I added the following at the end of my processArgs() method

    [[NSRunLoop currentRunLoop] run];
    
  • I discovered using QTTime objects to measure the amount of time I had recorded was more trouble than I needed to deal with, so instead I opted for a simple NSDate object (called mRecordingStartedOnDate). Also looking at my output it was clear that the session/camera take some time to warm up before capturing begins and the output is written to the file. So I check the recordedFileSize on the file first, and start counting time from then. All this happens in my captureOutput delegate method like so;

    - (void)captureOutput:(QTCaptureFileOutput *)captureOutput didOutputSampleBuffer:(QTSampleBuffer *)sampleBuffer fromConnection:(QTCaptureConnection *)connection {
        // check we have started to record some bytes (this allows us to wait for camera to warm up and start capturing)
        long recordedBytes = [mCaptureMovieFileOutput recordedFileSize];
    
        if(mRecordingStartedOnDate != nil) {
            // check if we have recorded enough video yet
            float recordedSeconds = [[NSDate date] timeIntervalSinceDate:mRecordingStartedOnDate];
            verbose("(recorded %f seconds)\n", recordedSeconds);
            if(recordedSeconds >= 5.0) {
                // stop recording to the file
                [mCaptureMovieFileOutput recordToOutputFileURL:nil];
            }
        } else if(recordedBytes > 0) {
            // set the start date when recording initiated
            mRecordingStartedOnDate = [NSDate date];
        }
    }
    

I may refactor things, but the code for the current state of this project is here.

-- Thanks to Paul Tsochantaris for the help!