3

my goal is to apply some filters on the camera input in real time. To do that step by step, I'm trying to get the input form the camera with AVFoundation record a video and save it in the camera roll. I tried, but for some reason the AVAssetWriter is always in AVAssetWriterStatusFailed and so the appendSampleBuffer: method always failed. Where is my error? Someone can help me?

Thanks!

ViewController.h

#import <UIKit/UIKit.h>
#import <AssetsLibrary/AssetsLibrary.h>
#import <AVFoundation/AVFoundation.h>

@interface ViewController : UIViewController <AVCaptureVideoDataOutputSampleBufferDelegate>
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property (weak, nonatomic) IBOutlet UIButton *startRecButton;
@property (weak, nonatomic) IBOutlet UIButton *stopRecButton;
@property (weak, nonatomic) IBOutlet UIButton *startVideocamera;
- (IBAction)startRecordingButtonPressed:(UIButton *)sender;
- (IBAction)stopRecordingButtonPressed:(UIButton *)sender;
- (IBAction)startVideocameraButtonPressed:(UIButton *)sender;

@end

ViewController.m

#import "ViewController.h"
@interface ViewController ()
@property (strong, nonatomic) AVAssetWriter* videoAssetWriter;
@property (strong, nonatomic) AVAssetWriterInput* videoAssetWriterInput;
@property (strong, nonatomic) NSURL* temporaryVideoURL;
@end


@implementation ViewController

#pragma mark - Variables
@synthesize imageView;
@synthesize videoAssetWriter;
@synthesize videoAssetWriterInput;
@synthesize temporaryVideoURL;
//initCaptureSession Method 
AVCaptureSession* captureSession;
AVCaptureDevice* videoCaptureDevice;
AVCaptureDeviceInput* videoCaptureDeviceInput;
AVCaptureVideoDataOutput* videoDataOutput;
dispatch_queue_t videoQueue;

//captureOutput:didOutputSampleBuffer Method
CMSampleBufferRef currentSampleBuffer;

BOOL isRecording;

//newPixelBufferFromCGImage Method
CGAffineTransform frameTransform;
CGSize frameSize;


 #pragma mark - User Interface

- (void)viewDidLoad
 {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (IBAction)startRecordingButtonPressed:(UIButton *)sender {
    [self initWriter];
}

- (IBAction)stopRecordingButtonPressed:(UIButton *)sender {
    [self stopWriter];
}

- (IBAction)startVideocameraButtonPressed:(UIButton *)sender {
    [self initCaptureSession];
}



#pragma mark - Capture Utils

-(void) initCaptureSession{

    captureSession = [[AVCaptureSession alloc] init];
    [captureSession setSessionPreset:AVCaptureSessionPreset1280x720];

    videoCaptureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    NSError* error;
    videoCaptureDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:videoCaptureDevice      error:&error];
    if([captureSession canAddInput:videoCaptureDeviceInput]){
        [captureSession addInput:videoCaptureDeviceInput];
    }
    videoDataOutput = [[AVCaptureVideoDataOutput alloc]init];
    [captureSession addOutput:videoDataOutput];
    videoQueue = dispatch_queue_create("videoQueue", NULL);
    [videoDataOutput setAlwaysDiscardsLateVideoFrames:NO];
    [videoDataOutput setSampleBufferDelegate:self queue:videoQueue];
    NSString* key = (NSString*)kCVPixelBufferPixelFormatTypeKey;
    NSNumber* value = [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA];
    NSDictionary* videoSettings = [NSDictionary dictionaryWithObject:value forKey:key];
    [videoDataOutput setVideoSettings:videoSettings];

    [captureSession startRunning]; 
}


-(void) captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:  (CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection{

    currentSampleBuffer = sampleBuffer;
    CGImageRef image = [self imageFromSampleBuffer:currentSampleBuffer];
    dispatch_sync(dispatch_get_main_queue(),
                  ^{
                      if(!isRecording){
                          imageView.image = [UIImage imageWithCGImage: image scale:1.0 orientation:UIImageOrientationRight];
                      }
                      else{
                          imageView.image = [UIImage imageWithCGImage: image scale:1.0 orientation:UIImageOrientationRight];
            //              [videoAssetWriterInput appendSampleBuffer:currentSampleBuffer];
                          if (![videoAssetWriterInput appendSampleBuffer:sampleBuffer]) {
                              [self showError:[videoAssetWriter error]];
                          }
                          NSLog(@"%ld", (long)[videoAssetWriter status]);
                      }
                });
    CGImageRelease(image);

}

-(void)captureOutput:(AVCaptureOutput *)captureOutput didDropSampleBuffer:  (CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection{

    NSLog(@"didDropSampleBuffer CALLED");

}



#pragma mark - Writer Utils

-(void) initWriter{ 
    temporaryVideoURL = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@%@", NSTemporaryDirectory(), @"Movie.MOV"]];
    NSLog(@"%@", temporaryVideoURL);
    NSError* error;
    videoAssetWriter = [[AVAssetWriter alloc] initWithURL:temporaryVideoURL fileType:AVFileTypeQuickTimeMovie error:&error];

    NSParameterAssert(videoAssetWriter);
    NSLog(@"%ld", (long)[videoAssetWriter status]);

    NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:
                              AVVideoCodecH264, AVVideoCodecKey,
                               [NSNumber numberWithInt:1280], AVVideoWidthKey,
                               [NSNumber numberWithInt:720], AVVideoHeightKey,
                               nil];
    videoAssetWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoSettings];

    NSParameterAssert(videoAssetWriterInput);
    NSLog(@"%ld", (long)[videoAssetWriter status]);

    if([videoAssetWriter canAddInput:videoAssetWriterInput]){
        [videoAssetWriter addInput:videoAssetWriterInput];
    }

    isRecording = YES;
    [videoAssetWriter startWriting];
    NSLog(@"%ld", (long)[videoAssetWriter status]);
}

-(void) stopWriter{
    [videoAssetWriterInput markAsFinished];
    [videoAssetWriter finishWritingWithCompletionHandler:^{

        NSLog(@"finishWritingWithCompletionHandler CALLED");
        isRecording = NO;
        [self saveVideoToCameraRoll];
        videoAssetWriter =nil;
        videoAssetWriterInput= nil;

    }];
//    [videoAssetWriter finishWriting];
//    isRecording = NO;
//    [self saveVideoToCameraRoll];


}

-(void) saveVideoToCameraRoll{

    ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
    [library writeVideoAtPathToSavedPhotosAlbum:temporaryVideoURL completionBlock:^(NSURL *assetURL, NSError *error){
        NSLog(@"ASSET URL: %@", [assetURL path]);

        if(error) {
            NSLog(@"CameraViewController: Error on saving movie : %@ {imagePickerController}", error);
        }
        else {
            NSLog(@"Video salvato correttamente in URL: %@", assetURL);
            BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:[temporaryVideoURL path]];
            NSLog(@"IL FILE ESISTE: %hhd", fileExists);
            NSLog(@"E PESA: %@", [[[NSFileManager defaultManager] attributesOfItemAtPath:  [temporaryVideoURL path] error:&error] objectForKey:NSFileSize]);
        }
    }];
}
accipiter
  • 33
  • 2
  • 10
  • `AVAssetWriter` has an `error` property that will help shed some light on what the problem is. https://developer.apple.com/library/ios/documentation/AVFoundation/Reference/AVAssetWriter_Class/Reference/Reference.html – jgh Mar 17 '14 at 23:30
  • the error log is: Error Domain=AVFoundationErrorDomain Code=-11823 "Cannot Save" UserInfo=0x1756f4c0 {NSLocalizedRecoverySuggestion=Try saving again., NSUnderlyingError=0x1766bec0 "The operation couldn’t be completed. (OSStatus error -12412.)", NSLocalizedDescription=Cannot Save} – accipiter Mar 24 '14 at 10:49
  • 3
    Does the file already exist? Is the specified file in a directory your app can write to? – jgh Mar 24 '14 at 15:36
  • Sorry for the late answer. That was right. Probably the file had been already created and so the assetWriter couldn't write the frames. – accipiter May 05 '14 at 09:49
  • I think I got around this issue by asking a file manager to create a directory structure for my recording path. From my understanding, iOS does not allow writing to the documents directory directly - you have to create a subfolder. – Alex Stone Jan 22 '16 at 22:19

1 Answers1

0

This error is because of a reason that a file with similar filename already exist.

In my case, I was using a static file name for testing purpose, which caused the error. Making it something unique something like: "\(Date().timeIntervalSince1970).mp4" fixed it.

khush
  • 540
  • 5
  • 16