參考的博客不過里面添加了一些我自己的總結,
#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
