參考的博客不過里面添加了一些我自己的總結,
#import "LittleVideoController.h" #import <AVKit/AVKit.h> #import <AVFoundation/AVFoundation.h> @interface LittleVideoController ()<AVCaptureFileOutputRecordingDelegate> @property(nonatomic,strong) dispatch_source_t timer; @property (weak, nonatomic) IBOutlet UIButton * startButton;//開始錄制 @property(nonatomic,strong) UIButton * videoButton;//播放 @property(nonatomic,strong) AVCaptureSession * captureSession;//捕捉會話對象 @property(nonatomic,strong) AVCaptureMovieFileOutput * captureMovieFileOutput;// @property(nonatomic,strong) AVCaptureVideoPreviewLayer * captureVideoPreviewLayer; @property(nonatomic,strong) NSString * videoPath; @end @implementation LittleVideoController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. [self.startButton addTarget:self action:@selector(startCaptureWithSession) forControlEvents:UIControlEventTouchUpInside]; /* 下面是一個有趣的寫法,上網搜到這樣一段話,補充說明之后能理解這種寫法: 這個問題嚴格上講和Objective-C沒什么太大的關系,這個是GNU C的對C的擴展語法 在理解一下什么是GNU C ,下面是百度給的定義: GNU C 函式庫(GNU C Library,又稱為glibc)是一種按照LGPL許可協議發布的,公開源代碼的,免費的,方便從網絡下載的C的編譯程序。 GNU C運行期庫,是一種C函式庫,是程序運行時使用到的一些API集合,它們一般是已預先編譯好,以二進制代碼形式存 在Linux類系統中,GNU C運行期庫,通常作為GNU C編譯程序的一個部分發布。 它最初是自由軟件基金會為其GNU操作系統所寫,但目前最主要的應用是配合Linux內核,成為GNU/Linux操作系統一個重要的組成部分。 繼續解釋: Xcode采用的Clang編譯,Clang作為GCC(GCC的初衷是為GNU操作系統專門編寫的一款編譯器)的替代品,和GCC一樣對於GNU C語法完全支持 你可能知道if(condition)后面只能根一條語句,多條語句必須用{}闊起來,這個語法擴展即將一條(多條要用到{})語句外面加一個括號(), 這樣的話你就可以在表達式中應用循環、判斷甚至本地變量等。 表達式()最后一行應該一個能夠計算結果的子表達式加上一個分號(;),這個子表達式作為整個結構的返回結果 這個擴展在代碼中最常見的用處在於宏定義中 */ self.captureSession = ({ // 分辨率設置 AVCaptureSession *session = [[AVCaptureSession alloc] init]; // 先判斷這個設備是否支持設置你要設置的分辨率 if ([session canSetSessionPreset:AVCaptureSessionPresetMedium]) { /* 下面是對你能設置的預設圖片的質量和分辨率的說明 AVCaptureSessionPresetHigh High 最高的錄制質量,每台設備不同 AVCaptureSessionPresetMedium Medium 基於無線分享的,實際值可能會改變 AVCaptureSessionPresetLow LOW 基於3g分享的 AVCaptureSessionPreset640x480 640x480 VGA AVCaptureSessionPreset1280x720 1280x720 720p HD AVCaptureSessionPresetPhoto Photo 完整的照片分辨率,不支持視頻輸出 */ [session setSessionPreset:AVCaptureSessionPresetMedium]; } session; }); // 初始化一個拍攝輸出對象 self.captureMovieFileOutput = ({ //輸出一個電影文件 /* a.AVCaptureMovieFileOutput 輸出一個電影文件 b.AVCaptureVideoDataOutput 輸出處理視頻幀被捕獲 c.AVCaptureAudioDataOutput 輸出音頻數據被捕獲 d.AVCaptureStillImageOutput 捕獲元數據 */ AVCaptureMovieFileOutput * output = [[AVCaptureMovieFileOutput alloc]init]; /* 一個ACCaptureConnection可以控制input到output的數據傳輸。 */ AVCaptureConnection * connection = [output connectionWithMediaType:AVMediaTypeVideo]; if ([connection isVideoMirroringSupported]) { /* 視頻防抖 是在 iOS 6 和 iPhone 4S 發布時引入的功能。到了 iPhone 6,增加了更強勁和流暢的防抖模式,被稱為影院級的視頻防抖動。相關的 API 也有所改動 (目前為止並沒有在文檔中反映出來,不過可以查看頭文件)。防抖並不是在捕獲設備上配置的,而是在 AVCaptureConnection 上設置。由於不是所有的設備格式都支持全部的防抖模式,所以在實際應用中應事先確認具體的防抖模式是否支持: typedef NS_ENUM(NSInteger, AVCaptureVideoStabilizationMode) { AVCaptureVideoStabilizationModeOff = 0, AVCaptureVideoStabilizationModeStandard = 1, AVCaptureVideoStabilizationModeCinematic = 2, AVCaptureVideoStabilizationModeAuto = -1, 自動 } NS_AVAILABLE_IOS(8_0) __TVOS_PROHIBITED; */ connection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeAuto; //預覽圖層和視頻方向保持一致 connection.videoOrientation = [self.captureVideoPreviewLayer connection].videoOrientation; } if ([self.captureSession canAddOutput:output]) { [self.captureSession addOutput:output]; } output; }); /* 用於展示制的畫面 */ self.captureVideoPreviewLayer = ({ AVCaptureVideoPreviewLayer * preViewLayer = [[AVCaptureVideoPreviewLayer alloc]initWithSession:self.captureSession]; preViewLayer.frame = CGRectMake(10, 50, 355, 355); /* AVLayerVideoGravityResizeAspect:保留長寬比,未填充部分會有黑邊 AVLayerVideoGravityResizeAspectFill:保留長寬比,填充所有的區域 AVLayerVideoGravityResize:拉伸填滿所有的空間 */ preViewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; [self.view.layer addSublayer:preViewLayer]; self.view.layer.masksToBounds = YES; preViewLayer; }); //如果這塊代碼寫在前面,則打開captureVideoPreviewLayer就顯示攝像頭的內容,和微信小程序一樣 if ([self SetSessioninputs:nil]) { [self.captureSession startRunning]; } } #pragma mark 開始錄制 -(void)startCaptureWithSession{ // 先刪除之前的視頻文件 if ([[NSFileManager defaultManager] fileExistsAtPath:self.videoPath]) { [[NSFileManager defaultManager] removeItemAtURL:[NSURL fileURLWithPath:self.videoPath] error:NULL]; } // NSError * error = nil; // if ([self SetSessioninputs:error]) { // // 開始錄制 [self startRecordSession]; // }else{ // // [self.captureSession stopRunning]; // NSLog(@"錄制失敗:%@",error); // } } -(BOOL)SetSessioninputs:(NSError *)error{ // capture 捕捉 捕獲 /* 視頻輸入類 AVCaptureDevice 捕獲設備類 AVCaptureDeviceInput 捕獲設備輸入類 */ AVCaptureDevice * captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; AVCaptureDeviceInput * videoInput = [AVCaptureDeviceInput deviceInputWithDevice: captureDevice error: &error]; if (!videoInput) { return NO; } // 給捕獲會話類添加輸入捕獲設備 if ([self.captureSession canAddInput:videoInput]) { [self.captureSession addInput:videoInput]; }else{ return NO; } // 添加音頻捕獲設備 /* __block AVCaptureDevice *backCamera = nil; NSArray *cameras = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; [cameras enumerateObjectsUsingBlock:^(AVCaptureDevice *camera, NSUInteger idx, BOOL * _Nonnull stop) { //AVCaptureDevicePositionFront 前攝像頭 //AVCaptureDevicePositionBack:后攝像頭 //AVCaptureDevicePositionUnspecified不指定前值或者后置,用系統當前的 if(camera.position == AVCaptureDevicePositionBack){ backCamera = camera; } }]; // 配置曝光模式 設置持續曝光模式 //注意改變設備屬性前一定要首先調用lockForConfiguration:調用完之后使用unlockForConfiguration方法解鎖 NSError *error = nil; [backCamera lockForConfiguration:&error]; //AVCaptureExposureModeLocked 直接用當前值就好,不指定 //AVCaptureExposureModeAutoExpose 自動調整曝光一次,然后用系統的 //AVCaptureExposureModeContinuousAutoExposure 需要的時候自行調節 //AVCaptureExposureModeCustom 自定義,需要自己手動設置 if ([backCamera isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]){ [backCamera setExposureMode:AVCaptureExposureModeContinuousAutoExposure]; } [backCamera unlockForConfiguration]; //閃光燈設計 AVCaptureFlashMode //手電筒開關--其實就是相機的閃光燈 AVCaptureTorchMode [backCamera lockForConfiguration:&error]; if([backCamera isTorchModeSupported:AVCaptureTorchModeOn]){ [backCamera setTorchMode:AVCaptureTorchModeOn]; } [backCamera unlockForConfiguration]; //焦距模式調整AVCaptureFocusMode //曝光量調節AVCaptureExposureMode //白平衡 AVCaptureWhiteBalanceMode //距離調整 AVCaptureAutoFocusRangeRestriction */ //如果需要指定的攝像頭, AVCaptureDevice *audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio]; // AVCaptureDeviceInput * audioInput = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:&error]; if (!audioDevice) { return NO; } if ([self.captureSession canAddInput:audioInput]) { [self.captureSession addInput:audioInput]; } return YES; } -(void)startRuningWithSession{ __block int time = 0; _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)); dispatch_source_set_timer(_timer, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC, 0 * NSEC_PER_SEC); dispatch_source_set_event_handler(_timer, ^{ time++; NSLog(@"錄制的時長:%d",time); if (time == 10) { NSLog(@"錄制的時長限制在10秒以內"); [self.captureMovieFileOutput stopRecording]; [self.captureSession stopRunning]; dispatch_source_cancel(_timer); } }); dispatch_resume(_timer); } #pragma mark -- #pragma mark -- AVCaptureMovieFileOutput 錄制視頻 -(void)startRecordSession{ [self.captureMovieFileOutput startRecordingToOutputFileURL:({ NSURL * url = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"zhangxu.mov"]]; NSLog(@"視頻想要緩存的地址:%@",url); if ([[NSFileManager defaultManager]fileExistsAtPath:url.path]) { [[NSFileManager defaultManager] removeItemAtURL:url error:nil]; } url; }) recordingDelegate:self]; } #pragma mark AVCaptureFileOutputRecordingDelegate 代理方法 - (void)captureOutput:(AVCaptureFileOutput *)captureOutput didStartRecordingToOutputFileAtURL:(NSURL *)fileURL fromConnections:(NSArray *)connections{ //Recording started NSLog(@"視頻錄制開始!!"); [self startRuningWithSession]; // 開始計時 } - (void)captureOutput:(AVCaptureFileOutput *)output didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray<AVCaptureConnection *> *)connections error:(nullable NSError *)error{ NSLog(@"視頻錄制結束!!"); NSLog(@"視頻緩存地址:%@",outputFileURL); NSLog(@"視頻壓縮前大小 %f M", [self getFileSize:[outputFileURL path]]); BOOL recordedSuccessfully = YES; id captureResult = [[error userInfo]objectForKey:AVErrorRecordingSuccessfullyFinishedKey]; if (captureResult) { recordedSuccessfully = [captureResult boolValue]; } if (recordedSuccessfully) { [self compressVideoWithFileUrl:outputFileURL]; } } //此⽅方法可以獲取視頻⽂文件的大小。 - (CGFloat) getFileSize:(NSString *)path { NSData * data = [NSData dataWithContentsOfFile:path]; float dataSize = (float)data.length/1024/1024; NSLog(@"視頻壓縮qian大小 %f M", dataSize); return dataSize; // NSLog(@"%@",path); // NSFileManager *fileManager = [NSFileManager defaultManager]; // float filesize = -1.0; // if ([fileManager fileExistsAtPath:path]) { // NSDictionary *fileDic = [fileManager attributesOfItemAtPath:path error:nil];//獲取⽂文件的屬性 // unsigned long long size = [[fileDic objectForKey:NSFileSize] longLongValue]; // filesize = 1.0*size/1024/1024; // }else{ NSLog(@"找不不到⽂文件"); // // // } // return filesize; } #pragma mark -- #pragma mark -- 視頻壓縮方法 -(void)compressVideoWithFileUrl:(NSURL *)fileUrl{ /* 這里需要注意的一點就是在重復的路徑上保存文件是不行的,可以選擇在點擊開始的時候刪除之前的 也可以這樣按照時間命名不同的文件保存 在后面的AVAssetWriter也要注意這一點 */ // 壓縮后的視頻的方法命名 NSDateFormatter * formatter = [[NSDateFormatter alloc]init]; [formatter setDateFormat:@"yyyy-MM-dd-HH:mm:ss"]; // 壓縮后的文件路徑 self.videoPath = [ NSString stringWithFormat:@"%@/%@.mov",NSTemporaryDirectory(),[formatter stringFromDate:[NSDate date]]]; // 先根據你傳入的文件的路徑穿件一個AVAsset AVAsset * asset = [AVAsset assetWithURL:fileUrl]; /* 根據urlAsset創建AVAssetExportSession壓縮類 第二個參數的意義:常用 壓縮中等質量 AVAssetExportPresetMediumQuality AVF_EXPORT NSString *const AVAssetExportPresetLowQuality NS_AVAILABLE_IOS(4_0); AVF_EXPORT NSString *const AVAssetExportPresetMediumQuality NS_AVAILABLE_IOS(4_0); AVF_EXPORT NSString *const AVAssetExportPresetHighestQuality NS_AVAILABLE_IOS(4_0); */ AVAssetExportSession * exportSession = [[AVAssetExportSession alloc]initWithAsset:asset presetName:AVAssetExportPresetMediumQuality]; // 優化壓縮,這個屬性能使壓縮的質量更好 exportSession.shouldOptimizeForNetworkUse = YES; // 到處的文件的路徑 exportSession.outputURL = [NSURL fileURLWithPath:self.videoPath]; // 導出的文件格式 /*! @constant AVFileTypeMPEG4 mp4格式的 AVFileTypeQuickTimeMovie mov格式的 @abstract A UTI for the MPEG-4 file format. @discussion The value of this UTI is @"public.mpeg-4". Files are identified with the .mp4 extension. 可以看看這個outputFileType格式,比如AVFileTypeMPEG4也可以寫成public.mpeg-4,其他類似 */ exportSession.outputFileType = AVFileTypeQuickTimeMovie; NSLog(@"視頻壓縮后的presetName: %@",exportSession.presetName); // 壓縮的方法 export 導出 Asynchronously 異步 [exportSession exportAsynchronouslyWithCompletionHandler:^{ /* exportSession.status 枚舉屬性 typedef NS_ENUM(NSInteger, AVAssetExportSessionStatus) { AVAssetExportSessionStatusUnknown, AVAssetExportSessionStatusWaiting, AVAssetExportSessionStatusExporting, AVAssetExportSessionStatusCompleted, AVAssetExportSessionStatusFailed, AVAssetExportSessionStatusCancelled }; */ int exportStatus = exportSession.status; switch (exportStatus) { case AVAssetExportSessionStatusFailed: NSLog(@"壓縮失敗"); break; case AVAssetExportSessionStatusCompleted: { /* 壓縮后的大小 也可以利用exportSession的progress屬性,隨時監測壓縮的進度 */ NSData * data = [NSData dataWithContentsOfFile:self.videoPath]; float dataSize = (float)data.length/1024/1024; NSLog(@"視頻壓縮后大小 %f M", dataSize); } break; default: break; } }]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } /* #pragma mark - Navigation // In a storyboard-based application, you will often want to do a little preparation before navigation - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { // Get the new view controller using [segue destinationViewController]. // Pass the selected object to the new view controller. } */ @end