18

I'm using AVAssetWriter, and it is perfectly working on iOS6.

The problem is, when I called finishWritingWithCompletionHandler, the completion handler is not called on iOS7 GM.

I called markAsFinished, and even endSessionAtSourceTime before I call finishWritingWithCompletionHandler.

It works fine on iOS6.

And even more, on iOS7, it works some times, and then it doesn't work again.

I don't know why, but it works if I call the method using alert view. So I tried performSelectorOnMainThread and inBackground, but it didn't help.

Any ideas?

doubleDown
  • 8,048
  • 1
  • 32
  • 48
Seho Kim
  • 185
  • 1
  • 6

5 Answers5

38

Apparently you need to retain the assetWriter now.

You might try retaining with a strong property it and see if your completion handler gets called. (Be sure to nil that property in completion handler.)

Ray Fix
  • 5,575
  • 3
  • 28
  • 30
  • Thank you for your answer. I'll try and let you know. – Seho Kim Sep 19 '13 at 13:53
  • Thanks. This was frustrating me to no end. AVAssetWriter was spitting out unplayable videos with incomplete metadata until I found this nugget of wisdom. Works like a charm now. – jbcaveman Dec 06 '13 at 22:47
  • I have the exact same problem but what exactly can I do if my AVAssetWriter is inside a method. How can I retain it then? Though it should be strong by default, isn't it? Please advise. – Vad Mar 02 '14 at 19:59
  • I think @riskpp had a good idea to reference it inside of finishWithCompletionHandler. The block will retain it. – Ray Fix Mar 02 '14 at 20:34
  • Hopefully you are already referencing it inside the callback block. Be sure that you are checking assetWriter.error to ensure that the process was successful. If you do this, the reference will be maintained as already mentioned. – CaseyB Apr 03 '14 at 03:28
8

Ray Fix, you are right. We need to retain assetWriter. The easiest way is to use it inside the finishWritingWithCompletionHandler block:

        NSError *error = nil;

        AVAssetWriter *videoWriter = [[AVAssetWriter alloc] initWithURL:[NSURL fileURLWithPath:path]
                                                                   fileType:AVFileType3GPP
                                                                      error:&error];
        //startWriting, session etc.  

        [videoWriter finishWritingWithCompletionHandler:^{
                NSLog(@"%@",videoWriter);
                NSLog(@"Write Ended");
            }];
riskpp
  • 135
  • 5
  • 1
    If finishWritingCompletionHandler not fires it will leak this forever. It is better to use a property declared strong or make sure it won't release the videowriter using a different way. – automaticoo Mar 03 '14 at 16:54
3

Retaining the assetwriter is very important but I also ran into a very strange intermittent failure even though my assetwriters were all retained (and never reused). The problem also wasn't a file name collision or missing directory because my file names are all based on CACurrentMediaTime() and don't change directories.

It seems if you don't set endSessionAtSourceTime: for the assetwriter every time, there's a very rare (but reproducible) chance the completion handler for finishWritingWithCompletionHandler: won't ever be called. If you wait a few seconds and check on the status of the assetwriter, it will be in the AVAssetWriterStatusFailure state and the error will be a non-descript "An unknown error occurred (-12763)". Additionally, changing the file format for the assetwriter doesn't see to have an effect on this problem. Lastly, this problem is probably only an issue if you need to rapidly record movies over and over again (as the chance for failure is probably 1/15 - 1/20).

So, just be sure to store the presentation time stamp for the last sample you're passing to the assetwriter and to call endSessionAtSourceTime: with that sample time right before you're about to call finishWritingWithCompletionHandler:.

Mr. T
  • 12,795
  • 5
  • 39
  • 47
  • Same here! In heavy-writing cases (realtime capture+write) i see same behaviour - callback not called without any errors. Propers endSessionAtSourceTime fixed the problem, thanks! – IPv6 Feb 18 '15 at 13:58
3

This happen on ARC as well.

The simplest solution is to define properties of AVAssetWriter(and AVAssetReader I assume)

@property(nonatomic,strong) AVAssetWriter *assetWriter;
@property(nonatomic,strong) AVAssetReader *assetReader;

and then

self.assetWriter = [AVAssetWriter assetWriterWithURL:destURL
                                                       fileType:AVFileTypeWAVE
                                                          error:&assetError];

and in the completion block

              [assetWriterInput markAsFinished];
             [assetWriter finishWritingWithCompletionHandler:^{
                 [assetReader cancelReading];
                 completionBlock(self);

             }];
loretoparisi
  • 15,724
  • 11
  • 102
  • 146
1

It also can happen if destination directory does not exist. In this case writing works fine, but no file created and the block is not called.

AlexeyVMP
  • 2,386
  • 3
  • 24
  • 31
  • I think this is what's happening to me. I am writing an app that records multiple video files in a row and I use the same code to set the new fileURLs for each new video. Sometimes the file is created and the block is called, other times the file is empty and the completion block never gets called and my assetwriter fails on the next video. I tried Ray's solution but that didn't work. Do you by any chance know what my problem could be? – Eric Appel Mar 13 '14 at 05:24
  • If your file empty it means that you didn't write any samples to it, or some error happened. I suggest to track number of samples successfully written to a file, it file is empty - don't wait for handler. – AlexeyVMP Mar 13 '14 at 06:10
  • In my case it's virtually impossible to have empty output file since I perform transcoding. – AlexeyVMP Mar 13 '14 at 06:12
  • 1
    Figured it out. I was setting the assetWriter to nil before the completion handler finished. So, if the block finished before my 'nil' call, it would write the file, otherwise I'd be left with an empty asset when going through the URLs in playback. – Eric Appel Mar 13 '14 at 16:44
  • @EricAppel I have the same usage pattern and saw the same problem. I found that it helped to call `endSessionAtSourceTime:`. See my answer if you're still faced with intermittent assetwriter failures – Mr. T May 30 '14 at 23:35