Save recorded video from AVFoundation - ios

I am not able to save the recorder video from AVFoundation... In didfinishcapture I check if file exists in the temporary folder, code always returns NO.
Also, this warning is printed when i stop the recording:
"cannot be saved to the saved photos album: Error Domain=NSOSStatusErrorDomain Code=2 "This movie could not be played." UserInfo=0x1c5696c0 {NSLocalizedDescription=This movie could not be played.}"
#define OP_PATH [NSTemporaryDirectory() stringByAppendingPathComponent:[#"movie" stringByAppendingPathExtension:#"mov"]]
- (IBAction) startSession:(id)sender
{
if(! self.captureSession)
{
//Session
self.captureSession = [[AVCaptureSession alloc] init];
//self.captureSession.sessionPreset = AVCaptureSessionPresetMedium;
//Layer of own view
CALayer *viewLayer = self.captureView.layer;
//AVCaptureVideoPreviewLayer
AVCaptureVideoPreviewLayer *avCaptureLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.captureSession];
avCaptureLayer.frame = self.captureView.bounds;
[self.captureView.layer addSublayer:avCaptureLayer];
//AVCaptureDevice
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
NSError *err = nil;
//Output - Image
self.stillImgOutput = [[AVCaptureStillImageOutput alloc] init];
[self.stillImgOutput setOutputSettings:[NSDictionary dictionaryWithObjectsAndKeys:
AVVideoCodecJPEG, AVVideoCodecKey,
nil]];
[self.captureSession addOutput:self.stillImgOutput];
//Output - Video
self.movieOutput = [[AVCaptureMovieFileOutput alloc] init];
// NSString* key = (NSString*)kCVPixelBufferBytesPerRowAlignmentKey;
//
// NSNumber* value = [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA];
//
// NSDictionary* videoSettings = [NSDictionary dictionaryWithObject:value forKey:key];
if([self.captureSession canAddOutput:self.movieOutput])
{
NSLog(#"Movie out put added");
[self.captureSession addOutput:self.movieOutput];
}
else
{
NSLog(#"Cannot add movie out put");
}
//Input
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&err];
if(! input)
{
NSLog(#"Error no camera");
return;
}
if([self.captureSession canAddInput:input])
{
[self.captureSession addInput:input];
}
else
{
NSLog(#"Cannot add input. Check Output Settings");
}
}
if(! [self.captureSession isRunning])
{
[self.captureSession startRunning];
}
else
{
NSLog(#"Session already running");
}
}
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error
{
NSLog(#"Did stop recording to - %# \n Any error ? - %#", outputFileURL, [error description]);
if([[NSFileManager defaultManager] fileExistsAtPath:[outputFileURL absoluteString]])
{
NSLog(#"YES file exists");
}
else
{
NSLog(#"NO File does not exist");
}
if(UIVideoAtPathIsCompatibleWithSavedPhotosAlbum([outputFileURL absoluteString]))
{
NSLog(#"YES file is compatible to be saved in Album");
UISaveVideoAtPathToSavedPhotosAlbum([outputFileURL absoluteString], self, #selector(video:didFinishSavingWithError:contextInfo:), nil);
}
else
{
NSLog(#"NO File is not compatible");
}
}
- (void)video:(NSString *)videoPath didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
{
if(! error)
{
NSLog(#"Video Saved to Album");
}
else
{
NSLog(#"Video not saved to Album - %#", [error description]);
}
NSError *er;
[[NSFileManager defaultManager] removeItemAtPath:OP_PATH error:&er];
if(! er)
{
NSLog(#"Temporary file deleted");
}
else
{
NSLog(#"Temporary file not deleted - %#", [er description]);
}
}

You are missing the following piece of code. See below
//Use timestamp to get new movie name everytime you capture
NSString *timeStamp = [NSString stringWithFormat:#"%0.0f",[[NSDate date] timeIntervalSince1970] * 1000];
NSString *movieOutputUrl =[NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.mov",timeStamp]];
NSURL *url = [NSURL URLWithString:movieOutputUrl];
[self.movieOutput startRecordingToOutputFileURL:url recordingDelegate:self];
I hope it helps.
Cheers.

- (void)captureOutput:(AVCaptureFileOutput *)captureOutputdidFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error
{
if (error)
{
NSLog(#"%#", error);
}
UIBackgroundTaskIdentifier backgroundRecordingID = [self backgroundRecordingID];
[self setBackgroundRecordingID:UIBackgroundTaskInvalid];
[[[ALAssetsLibrary alloc] init] writeVideoAtPathToSavedPhotosAlbum:outputFileURL completionBlock:^(NSURL *assetURL, NSError *error) {
if (error)
{
NSLog(#"%#", error);
}
[[NSFileManager defaultManager] removeItemAtURL:outputFileURL error:nil];
if (backgroundRecordingID != UIBackgroundTaskInvalid)
{
[[UIApplication sharedApplication] endBackgroundTask:backgroundRecordingID];
}
}];
}
Hope can help.

Related

extract the view from ReplayKit without presenting the view controller in iOS objective c

i am using ReplayKit for screen recording and i need to get a hold of the video instead of presenting it in the viewController, i have tried the following:
- (void)stopScreenRecording {
RPScreenRecorder *sharedRecorder = RPScreenRecorder.sharedRecorder;
[sharedRecorder stopRecordingWithHandler:^(RPPreviewViewController *previewViewController, NSError *error) {
if (error) {
NSLog(#"stopScreenRecording: %#", error.localizedDescription);
}
if (previewViewController) {
previewViewController.previewControllerDelegate = self;
self.previewViewController = previewViewController;
// RPPreviewViewController only supports full screen modal presentation.
//self.previewViewController.modalPresentationStyle = UIModalPresentationFullScreen;
// [self presentViewController:previewViewController animated:YES completion:nil];
NSURL *aMovieUrl = [previewViewController valueForKey:#"movieURL"];
[self writeVideoToAlbum:aMovieUrl];
}
}];
}
- (void)writeVideoToAlbum:(NSURL *)assetURL{
__block PHObjectPlaceholder *placeholder;
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
PHAssetChangeRequest* createAssetRequest = [PHAssetChangeRequest creationRequestForAssetFromVideoAtFileURL:assetURL];
placeholder = [createAssetRequest placeholderForCreatedAsset];
} completionHandler:^(BOOL success, NSError *error) {
if (success)
{
NSLog(#"Video successfully saved!");
}
else
{
NSLog(#"%#", error);
}
}];
}
but it doesn't seem to work, any suggestions would be much appreciated.
You can use following function to record screen with ReplayKit, will write video into NSDocumentDirectory.
For following code you can record video screens as well.
#property (strong, nonatomic) RPScreenRecorder *screenRecorder;
#property (strong, nonatomic) AVAssetWriter *assetWriter;
#property (strong, nonatomic) AVAssetWriterInput *assetWriterInput;
- (IBAction)startScreenRecording:(UIButton *)button {
self.screenRecorder = [RPScreenRecorder sharedRecorder];
if (self.screenRecorder.isRecording) {
return;
}
NSError *error = nil;
NSArray *pathDocuments = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *outputURL = pathDocuments[0];
NSString *videoOutPath = [[outputURL stringByAppendingPathComponent:[NSString stringWithFormat:#"%u", arc4random() % 1000]] stringByAppendingPathExtension:#"mp4"];
self.assetWriter = [AVAssetWriter assetWriterWithURL:[NSURL fileURLWithPath:videoOutPath] fileType:AVFileTypeMPEG4 error:&error];
NSDictionary *compressionProperties = #{AVVideoProfileLevelKey : AVVideoProfileLevelH264HighAutoLevel,
AVVideoH264EntropyModeKey : AVVideoH264EntropyModeCABAC,
AVVideoAverageBitRateKey : #(1920 * 1080 * 11.4),
AVVideoMaxKeyFrameIntervalKey : #60,
AVVideoAllowFrameReorderingKey : #NO};
NSNumber* width= [NSNumber numberWithFloat:self.view.frame.size.width];
NSNumber* height = [NSNumber numberWithFloat:self.view.frame.size.height];
if (#available(iOS 11.0, *)) {
NSDictionary *videoSettings = #{AVVideoCompressionPropertiesKey : compressionProperties,
AVVideoCodecKey : AVVideoCodecTypeH264,
AVVideoWidthKey : width,
AVVideoHeightKey : height};
self.assetWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoSettings];
} else {
// Fallback on earlier versions
}
[self.assetWriter addInput:self.assetWriterInput];
[self.assetWriterInput setMediaTimeScale:60];
[self.assetWriter setMovieTimeScale:60];
[self.assetWriterInput setExpectsMediaDataInRealTime:YES];
if (#available(iOS 11.0, *)) {
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
dispatch_async(dispatch_get_main_queue(), ^{
if (granted)
{
[self.screenRecorder setMicrophoneEnabled:YES];
[self.screenRecorder startCaptureWithHandler:^(CMSampleBufferRef _Nonnull sampleBuffer, RPSampleBufferType bufferType, NSError * _Nullable error) {
if (CMSampleBufferDataIsReady(sampleBuffer)) {
if (self.assetWriter.status == AVAssetWriterStatusUnknown && bufferType == RPSampleBufferTypeVideo) {
[self.assetWriter startWriting];
[self.assetWriter startSessionAtSourceTime:CMSampleBufferGetPresentationTimeStamp(sampleBuffer)];
}
if (self.assetWriter.status == AVAssetWriterStatusFailed) {
NSLog(#"An error occured.");
//show alert
[[RPScreenRecorder sharedRecorder] stopCaptureWithHandler:^(NSError * _Nullable error) {}];
return;
}
if (bufferType == RPSampleBufferTypeVideo) {
if (self.assetWriterInput.isReadyForMoreMediaData) {
[self.assetWriterInput appendSampleBuffer:sampleBuffer];
}else{
NSLog(#"Not ready for video");
}
}
}
} completionHandler:^(NSError * _Nullable error) {
if (!error) {
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setActive:YES error:nil];
// Start recording
NSLog(#"Recording started successfully.");
}else{
//show alert
}
}];
}
});
}];
} else {
// Fallback on earlier versions
}
}
- (IBAction)stopScreenRecording:(UIButton *)button {
if (#available(iOS 11.0, *)) {
dispatch_async(dispatch_get_main_queue(), ^{
[[RPScreenRecorder sharedRecorder] stopCaptureWithHandler:^(NSError * _Nullable error) {
if (!error) {
NSLog(#"Recording stopped successfully. Cleaning up...");
[self.assetWriterInput markAsFinished];
[self.assetWriter finishWritingWithCompletionHandler:^{
NSLog(#"File Url: %#",self.assetWriter.outputURL);
self.assetWriterInput = nil;
self.assetWriter = nil;
self.screenRecorder = nil;
}];
}
}];
});
} else {
// Fallback on earlier versions
NSLog(#"hello");
}
}

How to play AAC encoded data in iOS?

I am getting AAC encoded data from AACEncoder, I want to play that data however I received it from encoder completion block. Here is my code to set up Audio
- (void) setupAudioCapture {
if (_aacEncoder == nil) {
_aacEncoder = [[AACEncoder alloc] init];
/*
* Create audio connection
*/
AVCaptureDevice *audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
NSError *error = nil;
AVCaptureDeviceInput *audioInput = [[AVCaptureDeviceInput alloc] initWithDevice:audioDevice error:&error];
if (error) {
NSLog(#"Error getting audio input device: %#", error.description);
}
if ([self.captureSession canAddInput:audioInput]) {
[self.captureSession addInput:audioInput];
}
_audioQueue = dispatch_queue_create("Audio Capture Queue", DISPATCH_QUEUE_SERIAL);
_audioOutput = [[AVCaptureAudioDataOutput alloc] init];
[_audioOutput setSampleBufferDelegate:self queue:_audioQueue];
if ([self.captureSession canAddOutput:_audioOutput]) {
[self.captureSession addOutput:_audioOutput];
}
_audioConnection = [_audioOutput connectionWithMediaType:AVMediaTypeAudio];
}
}
Here I get AAC Encoded data from
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
if (connection == _audioConnection) {
CMTime pts = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
double dPTS = (double)(pts.value) / pts.timescale;
[_aacEncoder encodeSampleBuffer:sampleBuffer completionBlock:^(NSData *encodedData, NSError *error) {
if (encodedData) {
//What to do here to play this data?
}
} else {
NSLog(#"Error encoding AAC: %#", error);
}
}];
}}

Record video with AVAssetWriter

I'm trying to record video with AVAssetwriter but I keep getting NO when checking AVAssetWriterInput property readyForMoreMediaData before appending data.
I saw related posts that mentioned similar problem when trying to record audio + video so I took out the audio recording part but the problem still occurs (readyForMoreMediaData is always NO).
My code:
- (void)startRecordingWithAssetWriter {
NSLog(#"Setting up capture session");
captureSession = [[AVCaptureSession alloc] init];
//----- ADD INPUTS -----
NSLog(#"Adding video input");
//ADD VIDEO INPUT
AVCaptureDevice *videoCaptureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if (videoCaptureDevice) {
NSError *error;
videoInputDevice = [AVCaptureDeviceInput deviceInputWithDevice:videoCaptureDevice error:&error];
if (!error) {
if ([captureSession canAddInput:videoInputDevice]) {
[captureSession addInput:videoInputDevice];
} else {
NSLog(#"Couldn't add video input");
}
} else {
NSLog(#"Couldn't create video input");
}
} else {
NSLog(#"Couldn't create video capture device");
}
//ADD AUDIO INPUT
NSLog(#"Adding audio input");
AVCaptureDevice *audioCaptureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
NSError *error = nil;
AVCaptureDeviceInput *audioInput = [AVCaptureDeviceInput deviceInputWithDevice:audioCaptureDevice error:&error];
NSLog(#"Added audio input: %#", error.description);
if (audioInput) {
[captureSession addInput:audioInput];
}
//----- ADD OUTPUTS -----
captureQueue = dispatch_queue_create("com.recordingtest", DISPATCH_QUEUE_SERIAL);
//-- Create the output for the capture session.
videoOutput = [[AVCaptureVideoDataOutput alloc] init];
[videoOutput setAlwaysDiscardsLateVideoFrames:YES];
[videoOutput setVideoSettings:
[NSDictionary dictionaryWithObject:
[NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange]
forKey:(id)kCVPixelBufferPixelFormatTypeKey]];
[videoOutput setSampleBufferDelegate:self queue:captureQueue];
if ([captureSession canAddOutput:videoOutput]) {
NSLog(#"Added video Output");
[captureSession addOutput:videoOutput];
}
// audioOutput = [[AVCaptureAudioDataOutput alloc] init];
// [audioOutput setSampleBufferDelegate:self queue:captureQueue];
//
// if ([captureSession canAddOutput:audioOutput]) {
// NSLog(#"Added audio Output");
// [captureSession addOutput:audioOutput];
// }
//Create temporary URL to record to
NSString *outputPath = [[NSString alloc] initWithFormat:#"%#%#", NSTemporaryDirectory(), #"output.mp4"];
NSURL *outputURL = [[NSURL alloc] initFileURLWithPath:outputPath];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:outputPath])
{
NSError *error;
if ([fileManager removeItemAtPath:outputPath error:&error] == NO)
{
//Error - handle if requried
}
}
NSError *assetWriterError;
assetWriter = [AVAssetWriter assetWriterWithURL:outputURL fileType:AVFileTypeMPEG4 error:&assetWriterError];
if (assetWriterError) {
NSLog(#"Error Setting assetWriter: %#", assetWriterError);
}
if (assetWriter != nil) {
} else {
NSLog(#"Error Setting assetWriter: %#", assetWriterError);
}
assetWriterVideoIn = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:nil];
assetWriterVideoIn.expectsMediaDataInRealTime = YES;
// assetWriterAudioIn = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio outputSettings:nil];
// assetWriterAudioIn.expectsMediaDataInRealTime = YES;
isRecording = YES;
if ([assetWriter canAddInput:assetWriterVideoIn]) {
[assetWriter addInput:assetWriterVideoIn];
}
// if ([assetWriter canAddInput:assetWriterAudioIn]) {
// [assetWriter addInput:assetWriterAudioIn];
// }
[captureSession commitConfiguration];
[captureSession startRunning];
}
- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
fromConnection:(AVCaptureConnection *)connection {
NSLog(#"didOutputSampleBuffer");
CFRetain(sampleBuffer);
dispatch_async(captureQueue, ^{
if (assetWriter) {
if (isRecording) {
if (captureOutput == videoOutput) {
[self writeSampleBuffer:sampleBuffer ofType:AVMediaTypeVideo];
}
// else if (captureOutput == audioOutput) {
// [self writeSampleBuffer:sampleBuffer ofType:AVMediaTypeAudio];
// }
}
}
CFRelease(sampleBuffer);
});
}
- (void)writeSampleBuffer:(CMSampleBufferRef)sampleBuffer ofType:(NSString *)mediaType {
NSLog(#"writeSampleBuffer: %ld", (long) assetWriter.status);
CMTime presentationTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
if (assetWriter.status == AVAssetWriterStatusUnknown) {
if ([assetWriter startWriting]) {
NSLog(#"startSessionAtSourceTime");
[assetWriter startSessionAtSourceTime:presentationTime];
} else {
NSLog(#"Error writing initial buffer");
}
}
if (assetWriter.status == AVAssetWriterStatusWriting) {
if (mediaType == AVMediaTypeVideo) {
NSLog(#"assetWriterVideoIn.readyForMoreMediaData: %d", assetWriterVideoIn.readyForMoreMediaData);
if (assetWriterVideoIn.readyForMoreMediaData) {
NSLog(#"appendSampleBuffer");
if (![assetWriterVideoIn appendSampleBuffer:sampleBuffer]) {
NSLog(#"Error writing video buffer");
}
}
}
// else if (mediaType == AVMediaTypeAudio) {
// if (assetWriterAudioIn.readyForMoreMediaData) {
//
// if (![assetWriterAudioIn appendSampleBuffer:sampleBuffer]) {
// NSLog(#"Error writing audio buffer");
// }
// }
// }
}
}
It finally worked after I set assetWriterVideoIn = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:nil];
to get some actual settings instead of nil.
Changed to this:
NSDictionary *settings = [NSDictionary dictionaryWithObjectsAndKeys:AVVideoCodecH264, AVVideoCodecKey,
[NSNumber numberWithInt:480], AVVideoWidthKey,[NSNumber numberWithInt:640], AVVideoHeightKey, nil];
assetWriterVideoIn = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:settings];

Saving to iCloud with saveToURL:forSaveOperation: completionHandler: fails

I am trying to save a Dictionary of objects to iCloud but when I do it method saveToURL:forSaveOperation: completionHandler: fails. I also tried to override:
- (BOOL)writeContents:(id)contents
andAttributes:(NSDictionary *)additionalFileAttributes
safelyToURL:(NSURL *)url
forSaveOperation:(UIDocumentSaveOperation)saveOperation
error:(NSError **)outError
and of course the super call also returns false. Yet, I would have liked to read the error, but when I try to have the localizedError the compiler reports an error claiming it is not a structure or union.
This is the full piece of code:
-(instancetype)initWithSingleton{
NSURL *ubiq = [[NSFileManager defaultManager]
URLForUbiquityContainerIdentifier:nil];
NSURL *ubiquitousPackage = [[ubiq URLByAppendingPathComponent:
#"Stops"] URLByAppendingPathComponent:kFILENAME];
NSLog(#"file url=%#", ubiquitousPackage);
self=[self initWithFileURL:ubiquitousPackage];
if (self!=nil){
self.favoriteStops=[[NSMutableDictionary alloc] init];
NSURL *ubiq = [[NSFileManager defaultManager]
URLForUbiquityContainerIdentifier:nil];
if (ubiq) {
NSLog(#"iCloud access at %#", ubiq);
[self loadDocument];
} else {
NSLog(#"No iCloud access");
}
}
return self;
}
#define kFILENAME #"favorite.dox"
- (void)loadData:(NSMetadataQuery *)query {
if ([query resultCount] == 1) {
NSMetadataItem *item = [query resultAtIndex:0];
NSURL *url = [item valueForAttribute:NSMetadataItemURLKey];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLResponse *response;
NSData *GETReply= [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
NSMutableDictionary* dict=[NSKeyedUnarchiver unarchiveObjectWithData:GETReply];
[self setFavoriteStops:dict];
NSLog(#"favorites: %#", favoriteStops);
[self openWithCompletionHandler:^(BOOL success) {
if (success) {
NSLog(#"iCloud document opened");
} else {
NSLog(#"failed opening document from iCloud");
}
}];
}
}
- (void)queryDidFinishGathering:(NSNotification *)notification {
NSMetadataQuery *query = [notification object];
[query disableUpdates];
[query stopQuery];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSMetadataQueryDidFinishGatheringNotification
object:query];
_query = nil;
[self loadData:query];
}
- (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName
error:(NSError **)outError
{
if ([contents length] > 0) {
[self setFavoriteStops:[NSKeyedUnarchiver unarchiveObjectWithData:contents]];
}
return YES;
}
- (BOOL)writeContents:(id)contents
andAttributes:(NSDictionary *)additionalFileAttributes
safelyToURL:(NSURL *)url
forSaveOperation:(UIDocumentSaveOperation)saveOperation
error:(NSError **)outError{
//logging
NSString *str;
str= [[NSString alloc] initWithData:contents encoding:NSUTF8StringEncoding];
NSLog(#"saving data %#", str);
//logging
NSMutableDictionary *dict=[NSKeyedUnarchiver unarchiveObjectWithData:contents];
NSLog(#"dict=%#", dict);
BOOL success= [super writeContents:contents
andAttributes:additionalFileAttributes
safelyToURL:url
forSaveOperation:saveOperation
error:outError];
NSLog(#"error :%#", outError.localizedDescription) //syntax error
return success;
}
-(void) save{
NSLog(#"file url=%#", [self fileURL]);
[self saveToURL:[self fileURL]
forSaveOperation:UIDocumentSaveForOverwriting
completionHandler:^(BOOL success) {
if (success) { //this returns false
[self openWithCompletionHandler:^(BOOL success) {
NSLog(#"new document saved on iCloud");
}];
} else {
NSLog(#"error in iCloud Saving");
}
}];
}
- (id)contentsForType:(NSString *)typeName error:(NSError **)outError
{
NSLog(#"favorite stops=%# class=%#", self.favoriteStops, [favoriteStops class]);
NSData *archivedData=[NSKeyedArchiver archivedDataWithRootObject:self.favoriteStops];
return archivedData;
}
When I log the url on which to save, that is:
file:///private/var/mobile/Library/Mobile%20Documents/iCloud~com~information~inArrivo/Stops/favorite.dox
And when I check the error on the debugger it is:
Error Domain=NSCocoaErrorDomain Code=4 "The operation couldn’t be
completed. (Cocoa error 4.)" UserInfo=0x17bd6cb0
{NSFileNewItemLocationKey=file:///private/var/mobile/Applications/445778BF-86AF-4DE3-9E1B-BAC8F79D14D0/tmp/(A%20Document%20Being%20Saved%20By%20In%20Arrivo%20HD)/favorite.dox,
NSFileOriginalItemLocationKey=file:///private/var/mobile/Library/Mobile%20Documents/iCloud~com~information~inArrivo/Stops/favorite.dox,
NSUnderlyingError=0x17bfa860 "The operation couldn’t be completed.
(Cocoa error 4.)",
NSURL=file:///private/var/mobile/Library/Mobile%20Documents/iCloud~com~information~inArrivo/Stops/favorite.dox}
How may I fix it or at least know more?
The TSI Apple team answered me and provided me with a working class. Upon checking their version with mine the change seems to boil down to an update to - (void)queryDidFinishGathering:(NSNotification *)notification; by adding:
if (query.results == 0)
{
[self save]; // no favorites file, so create one
}
to make it as follows:
- (void)queryDidFinishGathering:(NSNotification *)notification {
NSMetadataQuery *query = [notification object];
[query disableUpdates];
[query stopQuery];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSMetadataQueryDidFinishGatheringNotification
object:query];
//•• added
if (query.results == 0)
{
[self save]; // no favorites file, so create one
}
[self loadData:query];
_query = nil;
}

AVCaptureMovieFileOutput - no active/enabled connections

I am trying to record video in my iPhone app using AVFoundation.
But whenever I click the Record button app crashes with this message
-[AVCaptureMovieFileOutput startRecordingToOutputFileURL:recordingDelegate:] - no active/enabled
connections.
I know same question asked in SO, but none of its answers helped me.
My problem is the same code works with another application perfectly, and when I try using exactly same code in this app - crashes. But still photo capture is working fine.
Adding my codes here - please help me, Thanks in advance
-(void)viewDidLoad
{
[super viewDidLoad];
self.captureSession = [[AVCaptureSession alloc] init];
AVCaptureDevice *videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
AVCaptureDevice *audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
self.videoInput = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:nil];
self.audioInput = [[AVCaptureDeviceInput alloc] initWithDevice:audioDevice error:nil];
self.stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
NSDictionary *stillImageOutputSettings = [[NSDictionary alloc] initWithObjectsAndKeys:
AVVideoCodecJPEG, AVVideoCodecKey, nil];
[self.stillImageOutput setOutputSettings:stillImageOutputSettings];
self.movieOutput = [[AVCaptureMovieFileOutput alloc] init];
[self.captureSession addInput:self.videoInput];
[self.captureSession addOutput:self.stillImageOutput];
previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.captureSession];
UIView *aView = self.view;
previewLayer.frame = CGRectMake(70, 190, 270, 270);
[aView.layer addSublayer:previewLayer];
}
-(NSURL *) tempFileURL
{
NSString *outputPath = [[NSString alloc] initWithFormat:#"%#%#", NSTemporaryDirectory(), #"output.mov"];
NSURL *outputURL = [[NSURL alloc] initFileURLWithPath:outputPath];
NSFileManager *manager = [[NSFileManager alloc] init];
if ([manager fileExistsAtPath:outputPath])
{
[manager removeItemAtPath:outputPath error:nil];
}
return outputURL;
}
-(IBAction)capture:(id)sender
{
if (self.movieOutput.isRecording == YES)
{
[self.movieOutput stopRecording];
}
else
{
[self.movieOutput startRecordingToOutputFileURL:[self tempFileURL] recordingDelegate:self];
}
}
-(void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL
fromConnections:(NSArray *)connections
error:(NSError *)error
{
BOOL recordedSuccessfully = YES;
if ([error code] != noErr)
{
id value = [[error userInfo] objectForKey:AVErrorRecordingSuccessfullyFinishedKey];
if (value)
recordedSuccessfully = [value boolValue];
NSLog(#"A problem occurred while recording: %#", error);
}
if (recordedSuccessfully) {
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library writeVideoAtPathToSavedPhotosAlbum:outputFileURL
completionBlock:^(NSURL *assetURL, NSError *error)
{
UIAlertView *alert;
if (!error)
{
alert = [[UIAlertView alloc] initWithTitle:#"Video Saved"
message:#"The movie was successfully saved to you photos library"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil, nil];
}
else
{
alert = [[UIAlertView alloc] initWithTitle:#"Error Saving Video"
message:#"The movie was not saved to you photos library"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil, nil];
}
[alert show];
}
];
}
}
I had the same problem while changing videoDevice activeFormat and later wanted to record video. Because I was using best quality video I had to set sessionPreset to high, like following
_session.sessionPreset = AVCaptureSessionPresetHigh;
and it worked for me! :)
Because currently you don't have an active connection to record video to file.
Check connection active status before recording to output file:
AVCaptureConnection *c = [self.movieOutput connectionWithMediaType:AVMediaTypeVideo];
if (c.active) {
//connection is active
} else {
//connection is not active
//try to change self.captureSession.sessionPreset,
//or change videoDevice.activeFormat
}
If connection is not active, try to change captureSession.sessionPreset or videoDevice.activeFormat.
Repeat until you have set a valid format (that means c.active == YES). Then you can record video to output file.
Several things are missing in your code :
You forgot to add movieOutput to your captureSession
Same for your audioInput
All your session configuration needs to be encapsulated by [_captureSession beginConfiguration] and [_captureSession commitConfiguration]
For audio recording you need to set the AVAudioSession to the correct category.
Here is your code updated :
- (void)viewDidLoad
{
[super viewDidLoad];
NSError *error = nil;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryRecord
error:&error];
if(!error)
{
[[AVAudioSession sharedInstance] setActive:YES error:&error];
if(error) NSLog(#"Error while activating AudioSession : %#", error);
}
else
{
NSLog(#"Error while setting category of AudioSession : %#", error);
}
self.captureSession = [[AVCaptureSession alloc] init];
AVCaptureDevice *videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
AVCaptureDevice *audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
self.videoInput = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:nil];
self.audioInput = [[AVCaptureDeviceInput alloc] initWithDevice:audioDevice error:nil];
self.stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
NSDictionary *stillImageOutputSettings = [[NSDictionary alloc] initWithObjectsAndKeys:
AVVideoCodecJPEG, AVVideoCodecKey, nil];
[self.stillImageOutput setOutputSettings:stillImageOutputSettings];
self.movieOutput = [[AVCaptureMovieFileOutput alloc] init];
[self.captureSession beginConfiguration];
[self.captureSession addInput:self.videoInput];
[self.captureSession addInput:self.audioInput];
[self.captureSession addOutput:self.movieOutput];
[self.captureSession addOutput:self.stillImageOutput];
[self.captureSession commitConfiguration];
AVCaptureVideoPreviewLayer *previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.captureSession];
previewLayer.frame = CGRectMake(0, 0, 320, 500);
[self.view.layer addSublayer:previewLayer];
[self.captureSession startRunning];
}
- (IBAction)toggleRecording:(id)sender
{
if(!self.movieOutput.isRecording)
{
[self.recordButton setTitle:#"Stop" forState:UIControlStateNormal];
NSString *outputPath = [NSTemporaryDirectory() stringByAppendingPathComponent:#"output.mp4"];
NSFileManager *manager = [[NSFileManager alloc] init];
if ([manager fileExistsAtPath:outputPath])
{
[manager removeItemAtPath:outputPath error:nil];
}
[self.movieOutput startRecordingToOutputFileURL:[NSURL fileURLWithPath:outputPath]
recordingDelegate:self];
}
else
{
[self.recordButton setTitle:#"Start recording" forState:UIControlStateNormal];
[self.movieOutput stopRecording];
}
}
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error
{
NSLog(#"Did finish recording, error %# | path %# | connections %#", error, [outputFileURL absoluteString], connections);
}
Hope this helps
I find the reason of this error.
check your session's "setSessionPreset" setting,
photo's resolution setting is different from video,
for iPhone5, video resolution of the back camera is 1920*1080, the front camere is 1280*720, and photo's max resolution is 3264*2488,
so if you set error resolution to video, the connect will not be actived.

Resources