Camera application for iPhone / iPad is very convenient to use. The user can easily switch from photographing mode to video. In the video mode, the shooting time and only one button (Start / Stop) is shown. Unfortunately, when using the standard UIImagePickerController, there is no way to control the number of frames per second and some other parameters. I will show how to use the AVFoundation framework to access more advanced camera settings, such as frames per second, video quality, recording duration, video file size. The user will immediately see the video on the screen in the quality in which it will be saved.AVCaptureSession // AVCaptureVideoPreviewLayer // , AVCaptureDevice // / AVCaptureDeviceInput // / AVCaptureSession AVCaptureMovieFileOutput // AVCaptureSession #import <UIKit/UIKit.h> #import <Foundation/Foundation.h> #import <CoreMedia/CoreMedia.h> #import <AVFoundation/AVFoundation.h> #import <AudioToolbox/AudioToolbox.h> #import <AVKit/AVKit.h> @protocol VideoRecorderDelegate <NSObject> // , - (void)videoRecorderDidFinishRecordingVideoWithOutputPath:(NSString *)outputPath; // , - (void)videoRecorderDidCancelRecordingVideo; @end @interface VideoRecorderController : UIViewController @property (nonatomic, retain) NSString *outputPath; // @property (nonatomic, assign) id<VideoRecorderDelegate> delegate; @end #import "VideoRecorderController.h" #define TOTAL_RECORDING_TIME 60*20 // #define FRAMES_PER_SECOND 30 // #define FREE_DISK_SPACE_LIMIT 1024 * 1024 // () #define MAX_VIDEO_FILE_SIZE 160 * 1024 * 1024 // () #define CAPTURE_SESSION_PRESET AVCaptureSessionPreset352x288 // #define BeginVideoRecording 1117 // #define EndVideoRecording 1118 // @interface VideoRecorderController () <AVCaptureFileOutputRecordingDelegate> { BOOL WeAreRecording; // , AVCaptureSession *CaptureSession; AVCaptureMovieFileOutput *MovieFileOutput; AVCaptureDeviceInput *VideoInputDevice; } // @property (retain) IBOutlet UILabel *timeLabel; // @property (retain) IBOutlet UIButton *startButton; // Start / Stop @property (retain) IBOutlet UIImageView *circleImage; // Start @property (retain) IBOutlet UIButton *cancelButton; // Cancel @property (retain) IBOutlet UIButton *useVideoButton; // Use Video @property (retain) IBOutlet UIView *bottomView; // @property (retain) IBOutlet UIButton *playVideoButton; // Play Video - (IBAction)startStopButtonPressed:(id)sender; // Start / Stop - (IBAction)cancel:(id)sender; // Cancel - (IBAction)useVideo:(id)sender; // Use Video - (IBAction)playVideo:(id)sender; // Play Video @property (retain) AVCaptureVideoPreviewLayer *PreviewLayer; // @property (retain) NSTimer *videoTimer; @property (assign) NSTimeInterval elapsedTime; @end @implementation VideoRecorderController - (void)viewDidLoad { [super viewDidLoad]; self.outputPath = [[NSString alloc] initWithFormat:@"%@%@", NSTemporaryDirectory(), @"output.mov"]; [self deleteVideoFile]; [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(applicationDidEnterBackground:) name: UIApplicationDidEnterBackgroundNotification object: nil]; CaptureSession = [[AVCaptureSession alloc] init]; AVCaptureDevice *VideoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; if (VideoDevice) { NSError *error = nil; VideoInputDevice = [AVCaptureDeviceInput deviceInputWithDevice:VideoDevice error:&error]; if (!error) { [CaptureSession beginConfiguration]; if ([CaptureSession canAddInput:VideoInputDevice]) { [CaptureSession addInput:VideoInputDevice]; } [CaptureSession commitConfiguration]; } } AVCaptureDevice *audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio]; NSError *error = nil; AVCaptureDeviceInput *audioInput = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:&error]; if (audioInput) { [CaptureSession addInput:audioInput]; } [self setPreviewLayer:[[AVCaptureVideoPreviewLayer alloc] initWithSession:CaptureSession]]; [self.PreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill]; [self setupLayoutInRect:[[[self view] layer] bounds]]; UIView *CameraView = [[UIView alloc] init]; [[self view] addSubview:CameraView]; [self.view sendSubviewToBack:CameraView]; [[CameraView layer] addSublayer:self.PreviewLayer]; MovieFileOutput = [[AVCaptureMovieFileOutput alloc] init]; CMTime maxDuration = CMTimeMakeWithSeconds(TOTAL_RECORDING_TIME, FRAMES_PER_SECOND); MovieFileOutput.maxRecordedDuration = maxDuration; MovieFileOutput.maxRecordedFileSize = MAX_VIDEO_FILE_SIZE; MovieFileOutput.minFreeDiskSpaceLimit = FREE_DISK_SPACE_LIMIT; if ([CaptureSession canAddOutput:MovieFileOutput]) { [CaptureSession addOutput:MovieFileOutput]; } if ([CaptureSession canSetSessionPreset:CAPTURE_SESSION_PRESET]) { [CaptureSession setSessionPreset:CAPTURE_SESSION_PRESET]; } [self cameraSetOutputProperties]; } - (void)applicationDidEnterBackground:(UIApplication *)application { if (WeAreRecording) { [self stopRecording]; } } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; if (![[NSFileManager defaultManager] fileExistsAtPath:self.outputPath]) { WeAreRecording = NO; [CaptureSession startRunning]; } } - (BOOL)shouldAutorotate { return (CaptureSession.isRunning && !WeAreRecording); } - (UIInterfaceOrientationMask)supportedInterfaceOrientations { return (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscape | UIInterfaceOrientationMaskPortraitUpsideDown); } - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator { [self setupLayoutInRect:CGRectMake(0, 0, size.width, size.height)]; [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) { } completion:^(id<UIViewControllerTransitionCoordinatorContext> context) { [self cameraSetOutputProperties]; }]; } // // viewWillTransitionToSize, iOS 7 - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation { [self setupLayoutInRect:[[[self view] layer] bounds]]; [self cameraSetOutputProperties]; } // - (void)setupLayoutInRect:(CGRect)layoutRect { [self.PreviewLayer setBounds:layoutRect]; [self.PreviewLayer setPosition:CGPointMake(CGRectGetMidX(layoutRect), CGRectGetMidY(layoutRect))]; } // - (void)cameraSetOutputProperties { AVCaptureConnection *videoConnection = nil; for ( AVCaptureConnection *connection in [MovieFileOutput connections] ) { for ( AVCaptureInputPort *port in [connection inputPorts] ) { if ( [[port mediaType] isEqual:AVMediaTypeVideo] ) { videoConnection = connection; } } } if ([videoConnection isVideoOrientationSupported]) { if ([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationPortrait) { self.PreviewLayer.connection.videoOrientation = AVCaptureVideoOrientationPortrait; [videoConnection setVideoOrientation:AVCaptureVideoOrientationPortrait]; } else if ([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationPortraitUpsideDown) { self.PreviewLayer.connection.videoOrientation = AVCaptureVideoOrientationPortraitUpsideDown; [videoConnection setVideoOrientation:AVCaptureVideoOrientationPortraitUpsideDown]; } else if ([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationLandscapeLeft) { self.PreviewLayer.connection.videoOrientation = AVCaptureVideoOrientationLandscapeLeft; [videoConnection setVideoOrientation:AVCaptureVideoOrientationLandscapeLeft]; } else { self.PreviewLayer.connection.videoOrientation = AVCaptureVideoOrientationLandscapeRight; [videoConnection setVideoOrientation:AVCaptureVideoOrientationLandscapeRight]; } } } - (IBAction)startStopButtonPressed:(id)sender { if (!WeAreRecording) { [self startRecording]; } else { [self stopRecording]; } } - (IBAction)cancel:(id)sender { if ([CaptureSession isRunning]) { if (self.delegate) { [self.delegate videoRecorderDidCancelRecordingVideo]; } } else { self.circleImage.hidden = NO; self.startButton.hidden = NO; self.useVideoButton.hidden = YES; [self.cancelButton setTitle:@"Cancel" forState:UIControlStateNormal]; self.timeLabel.text = @"00:00"; self.elapsedTime = 0; [CaptureSession startRunning]; } } - (IBAction)useVideo:(id)sender { if (self.delegate) { [self.delegate videoRecorderDidFinishRecordingVideoWithOutputPath:self.outputPath]; } } - (IBAction)playVideo:(id)sender { if ([[NSFileManager defaultManager] fileExistsAtPath:self.outputPath]) { NSURL *outputFileURL = [[NSURL alloc] initFileURLWithPath:self.outputPath]; AVPlayer *player = [AVPlayer playerWithURL:outputFileURL]; AVPlayerViewController *controller = [[AVPlayerViewController alloc] init]; [self presentViewController:controller animated:YES completion:nil]; controller.player = player; controller.allowsPictureInPicturePlayback = NO; [player play]; } } // - (void)startRecording { // AudioServicesPlaySystemSound(BeginVideoRecording); WeAreRecording = YES; [self.cancelButton setHidden:YES]; [self.bottomView setHidden:YES]; [self.startButton setImage:[UIImage imageNamed:@"StopVideo"] forState:UIControlStateNormal]; self.timeLabel.text = @"00:00"; self.elapsedTime = 0; self.videoTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(updateTime) userInfo:nil repeats:YES]; // , , [self deleteVideoFile]; // NSURL *outputURL = [[NSURL alloc] initFileURLWithPath:self.outputPath]; [MovieFileOutput startRecordingToOutputFileURL:outputURL recordingDelegate:self]; } - (void)deleteVideoFile { NSFileManager *fileManager = [NSFileManager defaultManager]; if ([fileManager fileExistsAtPath:self.outputPath]) { NSError *error = nil; if ([fileManager removeItemAtPath:self.outputPath error:&error] == NO) { // } } } // - (void)stopRecording { // AudioServicesPlaySystemSound(EndVideoRecording); WeAreRecording = NO; [CaptureSession stopRunning]; self.circleImage.hidden = YES; self.startButton.hidden = YES; [self.cancelButton setTitle:@"Retake" forState:UIControlStateNormal]; [self.cancelButton setHidden:NO]; [self.bottomView setHidden:NO]; [self.startButton setImage:[UIImage imageNamed:@"StartVideo"] forState:UIControlStateNormal]; // [self.videoTimer invalidate]; self.videoTimer = nil; // [MovieFileOutput stopRecording]; } - (void)updateTime { self.elapsedTime += self.videoTimer.timeInterval; NSInteger seconds = (NSInteger)self.elapsedTime % 60; NSInteger minutes = ((NSInteger)self.elapsedTime / 60) % 60; self.timeLabel.text = [NSString stringWithFormat:@"%02ld:%02ld", (long)minutes, (long)seconds]; } - (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error { if (WeAreRecording) { [self stopRecording]; } BOOL RecordedSuccessfully = YES; if ([error code] != noErr) { // , , // , id value = [[error userInfo] objectForKey:AVErrorRecordingSuccessfullyFinishedKey]; if (value != nil) { RecordedSuccessfully = [value boolValue]; } } if (RecordedSuccessfully) { // , Use Video self.useVideoButton.hidden = NO; } } - (void)viewDidUnload { [super viewDidUnload]; CaptureSession = nil; MovieFileOutput = nil; VideoInputDevice = nil; } @end Source: https://habr.com/ru/post/316862/
All Articles