fixed ios camera timing (patch by Eduard Feicho)

This commit is contained in:
Vadim Pisarevsky 2012-08-22 13:33:13 +04:00
parent da5aaab277
commit e95bc7d502
2 changed files with 43 additions and 111 deletions

View File

@ -80,7 +80,6 @@
@property (nonatomic, retain) UIView* parentView; @property (nonatomic, retain) UIView* parentView;
- (void)pause;
- (void)start; - (void)start;
- (void)stop; - (void)stop;
- (void)switchCameras; - (void)switchCameras;
@ -121,6 +120,8 @@
AVAssetWriterInputPixelBufferAdaptor* recordPixelBufferAdaptor; AVAssetWriterInputPixelBufferAdaptor* recordPixelBufferAdaptor;
AVAssetWriter* recordAssetWriter; AVAssetWriter* recordAssetWriter;
CMTime lastSampleTime;
} }
@property (nonatomic, assign) id<CvVideoCameraDelegate> delegate; @property (nonatomic, assign) id<CvVideoCameraDelegate> delegate;
@ -134,6 +135,8 @@
- (void)adjustLayoutToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation; - (void)adjustLayoutToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation;
- (void)layoutPreviewLayer; - (void)layoutPreviewLayer;
- (void)saveVideo; - (void)saveVideo;
- (NSURL *)videoFileURL;
@end @end

View File

@ -99,89 +99,28 @@ static CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;};
[super start]; [super start];
if (self.recordVideo == YES) { if (self.recordVideo == YES) {
// [self.videoFileOutput startRecordingToOutputFileURL:[self tempFileURL] recordingDelegate:self];
NSError* error; NSError* error;
if ([[NSFileManager defaultManager] fileExistsAtPath:[self tempFileString]]) [[NSFileManager defaultManager] removeItemAtPath:[self tempFileString] error:&error]; if ([[NSFileManager defaultManager] fileExistsAtPath:[self videoFileString]]) {
[[NSFileManager defaultManager] removeItemAtPath:[self videoFileString] error:&error];
}
if (error == nil) { if (error == nil) {
NSLog(@"[Camera] Delete file %@", [self tempFileString]); NSLog(@"[Camera] Delete file %@", [self videoFileString]);
}
BOOL started = [self.recordAssetWriter startWriting];
[self.recordAssetWriter startSessionAtSourceTime:kCMTimeZero];
NSLog(@"[Camera] Session started? %d", started);
if (self.recordAssetWriter.status == AVAssetWriterStatusUnknown) {
NSLog(@"AVAssetWriter status: unknown");
} else if (self.recordAssetWriter.status == AVAssetWriterStatusWriting) {
NSLog(@"AVAssetWriter status: writing");
} else if (self.recordAssetWriter.status == AVAssetWriterStatusCompleted) {
NSLog(@"AVAssetWriter status: completed");
} else if (self.recordAssetWriter.status == AVAssetWriterStatusFailed) {
NSLog(@"AVAssetWriter status: failed");
} else if (self.recordAssetWriter.status == AVAssetWriterStatusCancelled) {
NSLog(@"AVAssetWriter status: cancelled");
}
if (self.recordAssetWriter.status != AVAssetWriterStatusWriting) {
NSLog(@"[Camera] Recording Error: asset writer status is not writing: %@", self.recordAssetWriter.error);
} else {
NSLog(@"[Camera] Recording started");
} }
} }
} }
- (void)pause;
{
[super pause];
if (self.recordVideo == YES) {
// [self.videoFileOutput stopRecording];
if (self.recordAssetWriter.status == AVAssetWriterStatusUnknown) {
NSLog(@"AVAssetWriter status: unknown");
} else if (self.recordAssetWriter.status == AVAssetWriterStatusWriting) {
NSLog(@"AVAssetWriter status: writing");
} else if (self.recordAssetWriter.status == AVAssetWriterStatusCompleted) {
NSLog(@"AVAssetWriter status: completed");
} else if (self.recordAssetWriter.status == AVAssetWriterStatusFailed) {
NSLog(@"AVAssetWriter status: failed");
} else if (self.recordAssetWriter.status == AVAssetWriterStatusCancelled) {
NSLog(@"AVAssetWriter status: cancelled");
}
if (self.recordAssetWriter.status == AVAssetWriterStatusWriting) {
[self.recordAssetWriter finishWriting];
NSLog(@"[Camera] recording stopped");
} else {
NSLog(@"[Camera] Recording Error: asset writer status is not writing");
}
}
}
- (void)stop; - (void)stop;
{ {
[super stop]; [super stop];
if (self.recordVideo == YES) { self.videoDataOutput = nil;
NSLog(@"recording stop"); if (videoDataOutputQueue) {
if (self.recordAssetWriter.status == AVAssetWriterStatusUnknown) { dispatch_release(videoDataOutputQueue);
NSLog(@"AVAssetWriter status: unknown");
} else if (self.recordAssetWriter.status == AVAssetWriterStatusWriting) {
NSLog(@"AVAssetWriter status: writing");
} else if (self.recordAssetWriter.status == AVAssetWriterStatusCompleted) {
NSLog(@"AVAssetWriter status: completed");
} else if (self.recordAssetWriter.status == AVAssetWriterStatusFailed) {
NSLog(@"AVAssetWriter status: failed");
} else if (self.recordAssetWriter.status == AVAssetWriterStatusCancelled) {
NSLog(@"AVAssetWriter status: cancelled");
} }
if (self.recordVideo == YES) {
if (self.recordAssetWriter.status == AVAssetWriterStatusWriting) { if (self.recordAssetWriter.status == AVAssetWriterStatusWriting) {
[self.recordAssetWriter finishWriting]; [self.recordAssetWriter finishWriting];
@ -195,11 +134,6 @@ static CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;};
self.recordPixelBufferAdaptor = nil; self.recordPixelBufferAdaptor = nil;
} }
self.videoDataOutput = nil;
if (videoDataOutputQueue) {
dispatch_release(videoDataOutputQueue);
}
[self.customPreviewLayer removeFromSuperlayer]; [self.customPreviewLayer removeFromSuperlayer];
self.customPreviewLayer = nil; self.customPreviewLayer = nil;
} }
@ -405,15 +339,6 @@ static CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;};
- (void)createVideoFileOutput; - (void)createVideoFileOutput;
{ {
/*
if (self.recordVideo == YES) {
self.videoFileOutput = [[AVCaptureMovieFileOutput alloc] init];
if ( [self.captureSession canAddOutput:self.videoFileOutput] ) {
[self.captureSession addOutput:self.videoFileOutput];
}
}
*/
/* Video File Output in H.264, via AVAsserWriter */ /* Video File Output in H.264, via AVAsserWriter */
NSLog(@"Create Video with dimensions %dx%d", self.imageWidth, self.imageHeight); NSLog(@"Create Video with dimensions %dx%d", self.imageWidth, self.imageHeight);
@ -428,19 +353,16 @@ static CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;};
self.recordAssetWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:outputSettings]; self.recordAssetWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:outputSettings];
/* I'm going to push pixel buffers to it, so will need a
AVAssetWriterPixelBufferAdaptor, to expect the same 32BGRA input as I've
asked the AVCaptureVideDataOutput to supply */
int pixelBufferFormat = (self.grayscaleMode == YES) ? kCVPixelFormatType_420YpCbCr8BiPlanarFullRange : kCVPixelFormatType_32BGRA; int pixelBufferFormat = (self.grayscaleMode == YES) ? kCVPixelFormatType_420YpCbCr8BiPlanarFullRange : kCVPixelFormatType_32BGRA;
self.recordPixelBufferAdaptor = self.recordPixelBufferAdaptor =
[[AVAssetWriterInputPixelBufferAdaptor alloc] [[AVAssetWriterInputPixelBufferAdaptor alloc]
initWithAssetWriterInput:self.recordAssetWriterInput initWithAssetWriterInput:self.recordAssetWriterInput
sourcePixelBufferAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:pixelBufferFormat], kCVPixelBufferPixelFormatTypeKey,nil]]; sourcePixelBufferAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:pixelBufferFormat], kCVPixelBufferPixelFormatTypeKey, nil]];
NSError* error = nil; NSError* error = nil;
NSLog(@"Create AVAssetWriter with url: %@", [self tempFileURL]); NSLog(@"Create AVAssetWriter with url: %@", [self videoFileURL]);
self.recordAssetWriter = [AVAssetWriter assetWriterWithURL:[self tempFileURL] self.recordAssetWriter = [AVAssetWriter assetWriterWithURL:[self videoFileURL]
fileType:AVFileTypeMPEG4 fileType:AVFileTypeMPEG4
error:&error]; error:&error];
if (error != nil) { if (error != nil) {
@ -448,10 +370,9 @@ static CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;};
} }
[self.recordAssetWriter addInput:self.recordAssetWriterInput]; [self.recordAssetWriter addInput:self.recordAssetWriterInput];
self.recordAssetWriterInput.expectsMediaDataInRealTime = NO; self.recordAssetWriterInput.expectsMediaDataInRealTime = YES;
NSLog(@"[Camera] created AVAssetWriter"); NSLog(@"[Camera] created AVAssetWriter");
} }
@ -580,8 +501,6 @@ static CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;};
} }
// render buffer // render buffer
dispatch_sync(dispatch_get_main_queue(), ^{ dispatch_sync(dispatch_get_main_queue(), ^{
self.customPreviewLayer.contents = (__bridge id)dstImage; self.customPreviewLayer.contents = (__bridge id)dstImage;
@ -589,16 +508,26 @@ static CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;};
if (self.recordVideo == YES) { if (self.recordVideo == YES) {
// a very dense way to keep track of the time at which this frame lastSampleTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
// occurs relative to the output stream, but it's just an example! // CMTimeShow(lastSampleTime);
if (self.recordAssetWriter.status != AVAssetWriterStatusWriting) {
// TODO reset frame number [self.recordAssetWriter startWriting];
static int64_t frameNumber = 0; [self.recordAssetWriter startSessionAtSourceTime:lastSampleTime];
if (self.recordAssetWriterInput.readyForMoreMediaData) { if (self.recordAssetWriter.status != AVAssetWriterStatusWriting) {
[self.recordPixelBufferAdaptor appendPixelBuffer:imageBuffer NSLog(@"[Camera] Recording Error: asset writer status is not writing: %@", self.recordAssetWriter.error);
withPresentationTime:CMTimeMake(frameNumber, self.defaultFPS)]; return;
} else {
NSLog(@"[Camera] Video recording started");
} }
frameNumber++; }
if (self.recordAssetWriterInput.readyForMoreMediaData) {
if (! [self.recordPixelBufferAdaptor appendPixelBuffer:imageBuffer
withPresentationTime:lastSampleTime] ) {
NSLog(@"Video Writing Error");
}
}
} }
@ -627,14 +556,14 @@ static CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;};
} }
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init]; ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:[self tempFileURL]]) { if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:[self videoFileURL]]) {
[library writeVideoAtPathToSavedPhotosAlbum:[self tempFileURL] [library writeVideoAtPathToSavedPhotosAlbum:[self videoFileURL]
completionBlock:^(NSURL *assetURL, NSError *error){}]; completionBlock:^(NSURL *assetURL, NSError *error){}];
} }
} }
- (NSURL *)tempFileURL; - (NSURL *)videoFileURL;
{ {
NSString *outputPath = [[NSString alloc] initWithFormat:@"%@%@", NSTemporaryDirectory(), @"output.mov"]; NSString *outputPath = [[NSString alloc] initWithFormat:@"%@%@", NSTemporaryDirectory(), @"output.mov"];
NSURL *outputURL = [NSURL fileURLWithPath:outputPath]; NSURL *outputURL = [NSURL fileURLWithPath:outputPath];
@ -647,7 +576,7 @@ static CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;};
- (NSString *)tempFileString; - (NSString *)videoFileString;
{ {
NSString *outputPath = [[NSString alloc] initWithFormat:@"%@%@", NSTemporaryDirectory(), @"output.mov"]; NSString *outputPath = [[NSString alloc] initWithFormat:@"%@%@", NSTemporaryDirectory(), @"output.mov"];
return outputPath; return outputPath;