18

The Apple docs seem to indicate that while recording video to a file, the app can change the URL on the fly with no problem. But I'm seeing a problem. When I try this, the recording delegate gets called with an error...

The operation couldn’t be completed. (OSStatus error -12780.) Info dictionary is: { AVErrorRecordingSuccessfullyFinishedKey = 0; }

(funky single quote in "couldn't" comes from logging [error localizedDescription])

Here's the code, which is basically tweaks to WWDC10 AVCam sample:

1) Start recording. Start timer to change the output URL every few seconds

- (void) startRecording
{
    // start the chunk timer
    self.chunkTimer = [NSTimer scheduledTimerWithTimeInterval:5
                                                       target:self
                                                     selector:@selector(chunkTimerFired:)
                                                     userInfo:nil
                                                      repeats:YES];

    AVCaptureConnection *videoConnection = [AVCamCaptureManager connectionWithMediaType:AVMediaTypeVideo fromConnections:[[self movieFileOutput] connections]];
    if ([videoConnection isVideoOrientationSupported]) {
        [videoConnection setVideoOrientation:[self orientation]];
    }

    if ([[UIDevice currentDevice] isMultitaskingSupported]) {
        [self setBackgroundRecordingID:[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{}]];
    }

    NSURL *fileUrl = [[ChunkManager sharedInstance] nextURL];
    NSLog(@"now recording to %@", [fileUrl absoluteString]);
    [[self movieFileOutput] startRecordingToOutputFileURL:fileUrl recordingDelegate:self];
}

2) When the timer fires, change the output file name without stopping recording

- (void)chunkTimerFired:(NSTimer *)aTimer {

    if ([[UIDevice currentDevice] isMultitaskingSupported]) {
        [self setBackgroundRecordingID:[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{}]];
    }

    NSURL *nextUrl = [self nextURL];
    NSLog(@"changing capture output to %@", [[nextUrl absoluteString] lastPathComponent]);

    [[self movieFileOutput] startRecordingToOutputFileURL:nextUrl recordingDelegate:self];
}

Note: [self nextURL] generates file urls like file-0.mov, file-5.mov, file-10.mov and so on.

3) This gets called each time the file changes, and every other invocation is an error...

- (void)              captureOutput:(AVCaptureFileOutput *)captureOutput
didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL
                    fromConnections:(NSArray *)connections
                              error:(NSError *)error
{
    id delegate = [self delegate];
    if (error && [delegate respondsToSelector:@selector(someOtherError:)]) {
        NSLog(@"got an error, tell delegate");
        [delegate someOtherError:error];
    }

    if ([self backgroundRecordingID]) {
        if ([[UIDevice currentDevice] isMultitaskingSupported]) {
            [[UIApplication sharedApplication] endBackgroundTask:[self backgroundRecordingID]];
        }
        [self setBackgroundRecordingID:0];
    }

    if ([delegate respondsToSelector:@selector(recordingFinished)]) {
        [delegate recordingFinished];
    }
}

When this runs, file-0 gets written, then we see error -12780 right after changing the url to file-5, file-10 gets written, then an error, then okay, and so on.

It's appears that changing the URL on the fly doesn't work, but it stops the writing which allows the next URL change to work.

Cœur
  • 37,241
  • 25
  • 195
  • 267
danh
  • 62,181
  • 10
  • 95
  • 136
  • 1
    @danh Do you need a frame accurate solution, or it would be fine for you if you loose some frames between videos? – Alladinian Jun 05 '12 at 08:23
  • 1
    @Alladinian - no loss, but I'd be much obliged if there was a slightly lossy idea that got me unstuck here. I have already tried calling stopRecording and starting the next segment in the delegate method. This works, but the frame loss is much too big... measured in seconds. – danh Jun 05 '12 at 15:14
  • 1
    @danh Well it's confirmed (by an Apple engineer) that frame accurate switching is impossible with `AVCaptureFileOutput` (although they avoid explaining why the, documented, url switching is not working at all...). They say that it can be achieved though by using `AVCaptureVideoDataOutput` and `AVAssetWriter` classes but the solution is pretty much hardcore since you have to coordinate audio/video. – Alladinian Jun 07 '12 at 20:28
  • @Alladinian - Thanks very much for the heads up. Still no action on my 2 week old DTS request, and I was beginning to fear what you have confirmed. I've been investigating work-arounds, and will post here if I come up with something decent. – danh Jun 08 '12 at 15:10
  • 1
    In docs you linked to the question there is always explicitly mentioned that no media samples will be discarded between in **Mac OS X**. – Rok Jarc Jun 09 '12 at 14:10
  • 2
    DTS replied yesterday after a very long delay acknowledging that it's a bug. I've attached details as an answer. – danh Jun 09 '12 at 15:25
  • Newer doc had the paragraph removed: https://developer.apple.com/documentation/avfoundation/avcapturefileoutput – Cœur Mar 26 '19 at 19:12

3 Answers3

8

Thanks all, for the review and good thoughts on this. Here's the word from Apple DTS...

I spoke with our AV Foundation engineers, and it is definitely a bug in that this method is not doing what the documentation says it should ("You do not need to call stopRecording before calling this method while another recording is in progress."). Please file a bug report using the Apple Bug Reporter (http://developer.apple.com/bugreporter/) so the team can investigate. Make sure and include your minimal project in the report.

I've filed this with Apple as bug 11632087

danh
  • 62,181
  • 10
  • 95
  • 136
  • 1
    Oh man. I am glad that someone actually confirmed the bug. I've probably tried more that 5 different approaches (hacks) to make that work (even with double sessions/file-outputs that juggle the video input back and forth - there is simply no way to have multiple files without loosing 1-3 seconds of video between them). At last I can rest now :P – Alladinian Jun 09 '12 at 15:33
  • It seems I can't see other people's submitted bugs. Has this been resolved with 01 November 2012 xcode update? – noRema Dec 10 '12 at 17:06
  • Just checked bugreport. Still open. – danh Dec 11 '12 at 00:14
  • Ok thanks, so are you just waiting for a response or did you find a work around? – noRema Dec 12 '12 at 09:11
  • I have a bad work-around: wait until the recording is finished, then carve up the file. It's slow. It can be done asynch, so the app can remain responsive, but for my app there's not much for the user to do until the files are ready. – danh Dec 12 '12 at 17:44
  • Any news on whether the bug has been fixed, @danh? I'm also having some trouble getting this to work. – Jonas Bylov Aug 10 '13 at 14:38
  • Sorry. Negative on that one. I forgot to specify a new delegate for each output. – Jonas Bylov Aug 10 '13 at 15:27
1

In the docs it's stated this:

If a file at the given URL already exists when capturing starts, recording to the new file will fail.

Are you sure you check that nextUrl is a non-existing file name?

Mihai Fratu
  • 7,579
  • 2
  • 37
  • 63
  • Thanks for the thought: I delete (and confirm) files in the tmp directory before beginning. – danh Jun 04 '12 at 14:01
0

According to the documentation, calling to 2 consecutive startRecordingToOutputFileURL is not supported.

you can read about it here

In iOS, this frame accurate file switching is not supported. You must call stopRecording before calling this method again to avoid any errors.

koby
  • 203
  • 2
  • 4