ソースを参照

新增短视频录制插件

liufl5 5 年 前
コミット
43e36d9163

+ 26 - 0
WadeMobileFunc/WadeMobileFunc/customVideo/CustomVideoViewController.h

@ -0,0 +1,26 @@
1
//
2
//  CustomVideoViewController.h
3
//  WadeMobileFunc
4
//
5
//  Created by Mac on 2019/11/5.
6
//  Copyright © 2019 asiainfo. All rights reserved.
7
//
8
9
#import <UIKit/UIKit.h>
10
11
@protocol CustomVideoViewControllerDelegate<NSObject>
12
13
-(void)customWithBaseView:(UIView *)baseView VideoView:(UIView *)videoView SourceBundle:(NSBundle *)bundle;
14
@required
15
-(void)confirmWithData:(NSData *)data FilePath:(NSString *)filepath;
16
17
@end
18
19
@interface CustomVideoViewController : UIViewController
20
@property(nonatomic,weak)id<CustomVideoViewControllerDelegate> delegate;
21
22
-(id)initVCWithRecordSeconds:(int)senconds;
23
24
@end
25
26

+ 806 - 0
WadeMobileFunc/WadeMobileFunc/customVideo/CustomVideoViewController.m

@ -0,0 +1,806 @@
1
//
2
//  CustomVideoViewController.m
3
//  WadeMobileFunc
4
//
5
//  Created by Mac on 2019/11/5.
6
//  Copyright © 2019 asiainfo. All rights reserved.
7
//
8
9
#import "CustomVideoViewController.h"
10
#import <AVFoundation/AVFoundation.h>
11
#import <WadeMobile/WDCommonTool.h>
12
#import <WadeMobile/UIViewAdditions.h>
13
14
#define deviceHeight ([[UIScreen mainScreen] bounds].size.height)
15
#define deviceWidth ([[UIScreen mainScreen] bounds].size.width)
16
#define isIphoneX ([[UIScreen mainScreen] bounds].size.height==812?YES:NO)
17
#define widthScale(f) [CustomVideoViewController widthScale:f]
18
#define heightScale(f) [CustomVideoViewController heightScale:f]
19
20
typedef NS_ENUM(NSInteger,VideoStatus){
21
    VideoStatusEnded = 0,
22
    VideoStatusStarted
23
};
24
25
@interface CustomVideoViewController ()<AVCaptureFileOutputRecordingDelegate>
26
{
27
    AVCaptureSession *captureSession;
28
    AVCaptureDevice *videoDevice;//视频
29
    AVCaptureDevice *audioDevice;//音频
30
    AVCaptureDeviceInput *videoInput;//视频输入设备
31
    AVCaptureDeviceInput *audioInput;//音频输入设备
32
    AVCaptureMovieFileOutput *movieOutput;
33
    AVCaptureVideoPreviewLayer *captureVideoPreviewLayer;
34
    
35
    NSBundle *bundle;//资源包
36
    int seconds;//视频录制最大秒数
37
}
38
//拍摄区域
39
@property(nonatomic,strong) UIView *videoView;
40
41
@property(nonatomic,strong) UIView *bottomBaseView;
42
@property(nonatomic,strong) NSLayoutConstraint *progressWidth;//进度条约束
43
@property(nonatomic,strong) UIView *progressView;//进度条
44
@property(nonatomic,strong) UIView *recordView;//按住拍
45
@property(nonatomic,strong) UILabel *secondLbl;//当前录制秒数
46
@property(nonatomic,strong) UIButton *changeBtn;//前后摄像头切换
47
@property(nonatomic,strong) UIButton *backBtn;//返回
48
@property(nonatomic,strong) UIButton *confirmBtn;//确认录制完成
49
50
@property(nonatomic,assign) VideoStatus status;
51
@property(nonatomic,assign) BOOL canSave;//是否可以保存视频
52
53
@property(nonatomic,strong) CADisplayLink *link;//计时
54
55
@property(nonatomic,strong)NSString *validateData;
56
@end
57
58
@implementation CustomVideoViewController
59
60
-(id)initVCWithRecordSeconds:(int)senconds{
61
    self = [super init];
62
    if (self) {
63
        seconds = senconds;
64
    }
65
    return self;
66
}
67
68
- (void)viewDidLoad {
69
    [super viewDidLoad];
70
    self.view.backgroundColor = [UIColor whiteColor];
71
    
72
    NSString *bundlestring = [[[NSBundle mainBundle] bundlePath] stringByAppendingString:@"/WadeMobileFuncBundle.bundle"];
73
    bundle = [NSBundle bundleWithPath:bundlestring];
74
    
75
    //导航栏背景色
76
    UIImage * img = [WDCommonTool imageWithColor:[WDCommonTool convertHexToColor:@"#1e96fa"] andSize:CGSizeMake(deviceWidth, 64)];
77
    [self.navigationController.navigationBar setBackgroundImage:img forBarMetrics:UIBarMetricsDefault];
78
    
79
    //返回按钮
80
    UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
81
    backButton.frame = CGRectMake(0, 0, 30, 30);
82
    UIImage *backImg = [UIImage imageNamed:@"back" inBundle:bundle compatibleWithTraitCollection:nil];
83
    [backButton setImage:backImg forState:UIControlStateNormal];
84
    [backButton addTarget:self action:@selector(back:) forControlEvents:UIControlEventTouchUpInside];
85
    UIBarButtonItem *back = [[UIBarButtonItem alloc] initWithCustomView:backButton];
86
    self.navigationItem.leftBarButtonItem = back;
87
    
88
    //标题
89
    UILabel *titleLbl = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, deviceWidth, 30)];
90
    titleLbl.font = [UIFont systemFontOfSize:18];
91
    titleLbl.textAlignment = NSTextAlignmentCenter;
92
    self.navigationItem.titleView = titleLbl;
93
    titleLbl.text = @"自定义视频录制";
94
    titleLbl.textColor = [WDCommonTool convertHexToColor:@"#edfeff"];
95
    
96
    [self initUI];
97
    [self getAuthorization];
98
}
99
#pragma mark - UI初始化
100
/*
101
 UI初始化
102
 */
103
-(void)initUI{
104
    //录制区域
105
    self.videoView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, deviceWidth, deviceHeight-64)];
106
    self.videoView.layer.masksToBounds = YES;
107
    [self.view addSubview:self.videoView];
108
    
109
    //操作区域
110
    self.bottomBaseView = [[UIView alloc] initWithFrame:CGRectMake(0, deviceHeight-64-heightScale(100), deviceWidth, heightScale(100))];
111
    self.bottomBaseView.backgroundColor = [UIColor blackColor];
112
    self.bottomBaseView.alpha = 0.5;
113
    [self.view addSubview:self.bottomBaseView];
114
    
115
    self.progressView = [[UIView alloc] initWithFrame:CGRectMake(0, deviceHeight-heightScale(100)-64, 0, 2)];
116
    self.progressView.backgroundColor = [UIColor whiteColor];
117
    self.progressView.translatesAutoresizingMaskIntoConstraints = NO;
118
    [self.view addSubview:self.progressView];
119
    
120
    self.progressWidth = [NSLayoutConstraint constraintWithItem:self.progressView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:0];
121
    [self.progressView addConstraint:self.progressWidth];
122
    
123
    [self.progressView addConstraint:[NSLayoutConstraint constraintWithItem:self.progressView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:2]];
124
    
125
    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.progressView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeading multiplier:1 constant:0]];
126
    
127
    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.progressView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1 constant:deviceHeight-heightScale(100)-64]];
128
    
129
    
130
    UIButton *recordView11 = [UIButton buttonWithType:UIButtonTypeCustom];
131
    UIImage *recodeImg = [UIImage imageNamed:@"recode" inBundle:bundle compatibleWithTraitCollection:nil];
132
    [recordView11 setImage:recodeImg forState:UIControlStateNormal];
133
    recordView11.frame = CGRectMake(deviceWidth/2-widthScale(35), deviceHeight-64-self.bottomBaseView.height+10, widthScale(70), heightScale(70));
134
    [self.view addSubview:recordView11];
135
    self.recordView = [[UIView alloc] initWithFrame:CGRectMake(deviceWidth/2-widthScale(35), deviceHeight-64-self.bottomBaseView.height+10, widthScale(70), heightScale(70))];
136
    self.recordView.backgroundColor = [UIColor clearColor];
137
    self.recordView.userInteractionEnabled = YES;
138
    [self.view addSubview:self.recordView];
139
    
140
    self.backBtn = [UIButton buttonWithType:UIButtonTypeCustom];
141
    UIImage *backVideoImg = [UIImage imageNamed:@"backVideo" inBundle:bundle compatibleWithTraitCollection:nil];
142
    [self.backBtn setImage:backVideoImg forState:UIControlStateNormal];
143
    self.backBtn.frame = CGRectMake(self.recordView.left-widthScale(80), deviceHeight-64-self.bottomBaseView.height+10, widthScale(70), heightScale(70));
144
    [self.backBtn addTarget:self action:@selector(back:) forControlEvents:UIControlEventTouchUpInside];
145
    [self.view addSubview:self.backBtn];
146
    self.backBtn.hidden = YES;
147
    
148
    self.confirmBtn = [UIButton buttonWithType:UIButtonTypeCustom];
149
    UIImage *confirmImg = [UIImage imageNamed:@"confirm" inBundle:bundle compatibleWithTraitCollection:nil];
150
    [self.confirmBtn setImage:confirmImg forState:UIControlStateNormal];
151
    self.confirmBtn.frame = CGRectMake(self.recordView.right+widthScale(10), deviceHeight-64-self.bottomBaseView.height+10, widthScale(70), heightScale(70));
152
    [self.confirmBtn addTarget:self action:@selector(confirm:) forControlEvents:UIControlEventTouchUpInside];
153
    [self.view addSubview:self.confirmBtn];
154
    self.confirmBtn.hidden = YES;
155
    
156
    self.secondLbl = [[UILabel alloc] initWithFrame:CGRectMake(self.backBtn.left-widthScale(40)-15, deviceHeight-64-self.bottomBaseView.height+10, widthScale(40), heightScale(70))];
157
    self.secondLbl.font = [UIFont boldSystemFontOfSize:16];
158
    self.secondLbl.textColor = [UIColor whiteColor];
159
    self.secondLbl.text = @"";
160
    self.secondLbl.textAlignment = NSTextAlignmentRight;
161
    [self.view addSubview:self.secondLbl];
162
    
163
    self.changeBtn = [UIButton buttonWithType:UIButtonTypeCustom];
164
    UIImage *cameraImg = [UIImage imageNamed:@"camera" inBundle:bundle compatibleWithTraitCollection:nil];
165
    [self.changeBtn setImage:cameraImg forState:UIControlStateNormal];
166
    self.changeBtn.frame = CGRectMake(self.confirmBtn.right+10, deviceHeight-64-self.bottomBaseView.height+10, widthScale(40), heightScale(70));
167
    [self.changeBtn addTarget:self action:@selector(change:) forControlEvents:UIControlEventTouchUpInside];
168
    [self.view addSubview:self.changeBtn];
169
    
170
    if ([self.delegate respondsToSelector:@selector(customWithBaseView:VideoView:SourceBundle:)]) {
171
        [self.delegate customWithBaseView:self.view VideoView:self.videoView SourceBundle:bundle];
172
    }
173
    
174
    [self addGenstureRecognizer];
175
}
176
177
178
#pragma mark - click action
179
/*
180
 返回
181
 */
182
-(void)back:(UIButton *)sender{
183
    [self dismissViewControllerAnimated:YES completion:^{
184
        
185
    }];
186
}
187
/*
188
 确认压缩上传
189
 */
190
-(void)confirm:(UIButton *)sender{
191
    //    NSLog(@"开始压缩,压缩前大小 %f MB",[self fileSize:[self outPutFileURL]]);
192
    //媒体资源类
193
    AVURLAsset *avAsset = [[AVURLAsset alloc] initWithURL:[self outPutFileURL] options:nil];
194
    //使用AVAssetExportSession导出一个新的资源副本以及元数据改动.
195
    NSArray *compatiblePresets = [AVAssetExportSession exportPresetsCompatibleWithAsset:avAsset];
196
    if ([compatiblePresets containsObject:AVAssetExportPresetLowQuality]) {
197
        AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:avAsset presetName:AVAssetExportPreset640x480];
198
        [self removeFileWithUrl:[self compressedPath]];
199
        exportSession.outputURL = [self compressedURL];
200
        //优化网络
201
        exportSession.shouldOptimizeForNetworkUse = true;
202
        //转换后的格式
203
        exportSession.outputFileType = AVFileTypeMPEG4;//AVFileTypeQuickTimeMovie
204
        //异步导出
205
        [exportSession exportAsynchronouslyWithCompletionHandler:^{
206
            //如果导出的状态为完成
207
            if ([exportSession status] == AVAssetExportSessionStatusCompleted) {
208
                NSLog(@"压缩完毕,压缩后大小 %f MB",[self fileSize:[self compressedURL]]);
209
210
                if ([self.delegate respondsToSelector:@selector(confirmWithData:FilePath:)]) {
211
                    NSData *data = [NSData dataWithContentsOfURL:[self compressedURL]];
212
                    [self.delegate confirmWithData:data FilePath:[self compressedPath]];
213
                }
214
                [self dismissViewControllerAnimated:YES completion:^{
215
                    
216
                }];
217
            }else{
218
                NSLog(@"当前压缩进度:%f",exportSession.progress);
219
            }
220
        }];
221
    }
222
}
223
- (CGFloat)fileSize:(NSURL *)path
224
{
225
    return [[NSData dataWithContentsOfURL:path] length]/1024.00 /1024.00;
226
}
227
- (NSURL *)compressedURL
228
{
229
    return [NSURL fileURLWithPath:[self compressedPath]];
230
}
231
- (NSString *)compressedPath
232
{
233
    return [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true) lastObject] stringByAppendingPathComponent:[NSString stringWithFormat:@"compressed.mp4"]];
234
}
235
-(BOOL)removeFileWithUrl:(NSString *)url{
236
    NSFileManager *defaultManager = [NSFileManager defaultManager];
237
    NSError *error;
238
    [defaultManager removeItemAtPath:url error:&error];
239
    if (error) {
240
        NSLog(@"error = %@", error);
241
        return NO;
242
    }
243
    return YES;
244
}
245
/*
246
 前后摄像头切换
247
 */
248
-(void)change:(UIButton *)sender{
249
    switch (videoDevice.position) {
250
        case AVCaptureDevicePositionBack:
251
            videoDevice = [self deviceWithMediaType:AVMediaTypeVideo preferringPosition:AVCaptureDevicePositionFront];
252
            break;
253
        case AVCaptureDevicePositionFront:
254
            videoDevice = [self deviceWithMediaType:AVMediaTypeVideo preferringPosition:AVCaptureDevicePositionBack];
255
            break;
256
        default:
257
            return;
258
            break;
259
    }
260
    
261
    [self changeDevicePropertySafety:^(AVCaptureDevice *captureDevice) {
262
        NSError *error;
263
        AVCaptureDeviceInput *newVideoInput = [[AVCaptureDeviceInput alloc] initWithDevice:self->videoDevice error:&error];
264
        
265
        if (newVideoInput != nil) {
266
            //必选先 remove 才能询问 canAdd
267
            [self->captureSession removeInput:self->videoInput];
268
            if ([self->captureSession canAddInput:newVideoInput]) {
269
                [self->captureSession addInput:newVideoInput];
270
                self->videoInput = newVideoInput;
271
            }else{
272
                [self->captureSession addInput:self->videoInput];
273
            }
274
        } else if (error) {
275
            NSLog(@"切换前/后摄像头失败, error = %@", error);
276
        }
277
    }];
278
}
279
280
#pragma mark - 录制相关的方法
281
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
282
{
283
    UITouch *touch = [touches anyObject];
284
    CGPoint point = [touch locationInView:self.view];
285
    BOOL condition = [self isInBtnRect:point];//是否按住
286
    if (condition) {
287
        [self startAnimation];
288
    }else{
289
        [self stopAnimation];
290
    }
291
}
292
293
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
294
{
295
    [super touchesMoved:touches withEvent:event];
296
    UITouch *touch = [touches anyObject];
297
    CGPoint point = [touch locationInView:self.view];
298
    BOOL condition = [self isInBtnRect:point];
299
    if (!condition) {
300
        [self stopAnimation];
301
    }
302
}
303
304
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
305
{
306
    UITouch *touch = [touches anyObject];
307
    CGPoint point = [touch locationInView:self.view];
308
    BOOL condition = [self isInBtnRect:point];
309
    if (condition) {
310
        if (self.progressWidth.constant > deviceWidth*0.17) {
311
            [self recordComplete];//录制完成
312
        }
313
    }
314
    [self stopAnimation];
315
}
316
317
- (BOOL)isInBtnRect:(CGPoint)point
318
{
319
    CGFloat x = point.x;
320
    CGFloat y = point.y;
321
    return  (x>self.recordView.left && x<=self.recordView.right) && (y>self.recordView.top && y<=self.recordView.bottom);
322
}
323
324
- (void)startAnimation
325
{
326
    if (self.status == VideoStatusEnded) {
327
        self.status = VideoStatusStarted;
328
        [UIView animateWithDuration:0.5 animations:^{
329
            self.progressView.alpha = 1.0;
330
        } completion:^(BOOL finished) {
331
            [self stopLink];
332
            [self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
333
        }];
334
    }
335
}
336
- (CADisplayLink *)link
337
{
338
    if (!_link) {
339
        _link = [CADisplayLink displayLinkWithTarget:self selector:@selector(refresh:)];
340
        self.progressWidth.constant = 0;
341
        [self startRecord];
342
    }
343
    return _link;
344
}
345
- (void)refresh:(CADisplayLink *)link
346
{
347
    if (self.progressWidth.constant >= deviceWidth) {
348
        self.progressWidth.constant = deviceWidth;
349
        [self recordComplete];
350
        [self stopAnimation];
351
        return;
352
    }
353
    self.progressWidth.constant += deviceWidth/360;
354
    [self refreshSecond];
355
}
356
-(void)refreshSecond{//seconds
357
    CGFloat perWidth = deviceWidth/seconds;
358
    for (int i=1; i<seconds+1; i++) {
359
        if (self.progressWidth.constant>=perWidth*i-2 && self.progressWidth.constant<=perWidth*i+2) {
360
            self.secondLbl.text = [NSString stringWithFormat:@"%ds",i];
361
        }else if (self.progressWidth.constant==0){
362
            self.secondLbl.text = @"";
363
        }
364
    }
365
    
366
    
367
//    if (self.progressWidth.constant>=deviceWidth/6-2 && self.progressWidth.constant<=deviceWidth/6+2) {
368
//        self.secondLbl.text = @"1s";
369
//    }
370
//    else if (self.progressWidth.constant>=deviceWidth/3-2 && self.progressWidth.constant<=deviceWidth/3+2){
371
//        self.secondLbl.text = @"2s";
372
//    }
373
//    else if (self.progressWidth.constant>=deviceWidth/2-2 && self.progressWidth.constant<=deviceWidth/2+2){
374
//        self.secondLbl.text = @"3s";
375
//    }
376
//    else if (self.progressWidth.constant>=deviceWidth*2/3-2 && self.progressWidth.constant<=deviceWidth*2/3+2){
377
//        self.secondLbl.text = @"4s";
378
//    }
379
//    else if (self.progressWidth.constant>=deviceWidth*5/6-2 && self.progressWidth.constant<=deviceWidth*5/6+2){
380
//        self.secondLbl.text = @"5s";
381
//    }
382
//    else if (self.progressWidth.constant>=deviceWidth-2 && self.progressWidth.constant<=deviceWidth+2){
383
//        self.secondLbl.text = @"6s";
384
//    }
385
//    else if (self.progressWidth.constant==0){
386
//        self.secondLbl.text = @"";
387
//    }
388
}
389
- (void)startRecord
390
{
391
    [movieOutput startRecordingToOutputFileURL:[self outPutFileURL] recordingDelegate:self];
392
}
393
- (NSURL *)outPutFileURL
394
{
395
    NSLog(@"执行------------------------outPutFileURL");
396
    return [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@%@", NSTemporaryDirectory(), @"outPut.mp4"]];
397
}
398
- (void)stopLink
399
{
400
    _link.paused = YES;
401
    [_link invalidate];
402
    _link = nil;
403
}
404
- (void)recordComplete
405
{
406
    self.canSave = YES;
407
    self.backBtn.hidden = NO;
408
    self.confirmBtn.hidden = NO;
409
}
410
- (void)stopAnimation{
411
    if (self.status == VideoStatusStarted) {
412
        self.status = VideoStatusEnded;
413
        [self stopLink];
414
        [self stopRecord];
415
    }
416
}
417
- (void)stopRecord
418
{
419
    [movieOutput stopRecording];// 取消视频拍摄
420
}
421
422
423
#pragma mark - AVCaptureFileOutputRecordingDelegate
424
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didStartRecordingToOutputFileAtURL:(NSURL *)fileURL fromConnections:(NSArray *)connections
425
{
426
    NSLog(@"---- 开始录制 ----");
427
}
428
429
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error
430
{
431
    NSLog(@"---- 录制结束 ---%@-%@ ",outputFileURL,captureOutput.outputFileURL);
432
    
433
    if (outputFileURL.absoluteString.length == 0 && captureOutput.outputFileURL.absoluteString.length == 0 ) {
434
        [self showMsgWithTitle:@"出错了" andContent:@"录制视频保存地址出错"];
435
        return;
436
    }
437
    
438
    if (self.canSave) {
439
        self.canSave = NO;
440
    }
441
}
442
443
#pragma mark - 摄像头授权
444
/*
445
 获取授权
446
 */
447
- (void)getAuthorization
448
{
449
    switch ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo])
450
    {
451
        case AVAuthorizationStatusAuthorized://已授权,可使用
452
        {
453
            NSLog(@"授权摄像头使用成功");
454
            //视频录制初始化
455
            [self setupAVCaptureInfo];
456
            break;
457
        }
458
        case AVAuthorizationStatusNotDetermined://未进行授权选择
459
        {
460
            //则再次请求授权
461
            [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
462
                if(granted){//用户授权成功
463
                    //视频录制初始化
464
                    dispatch_async(dispatch_get_main_queue(), ^{
465
                        [self setupAVCaptureInfo];
466
                    });
467
                    return;
468
                }else{//用户拒绝授权
469
                    [self pop];
470
                    [self showMsgWithTitle:@"出错了" andContent:@"用户拒绝授权摄像头的使用权,返回上一页.请打开\n设置-->隐私/通用等权限设置"];
471
                    return;
472
                }
473
            }];
474
            break;
475
        }
476
        default://用户拒绝授权/未授权
477
        {
478
            [self pop];
479
            [self showMsgWithTitle:@"出错了" andContent:@"拒绝授权,返回上一页.请检查下\n设置-->隐私/通用等权限设置"];
480
            break;
481
        }
482
    }
483
}
484
/*
485
 视频录制初始化
486
 */
487
- (void)setupAVCaptureInfo
488
{
489
    //创建会话,并设置视频分辨率
490
    [self addSession];
491
    [captureSession beginConfiguration];
492
    //添加视频输入设备
493
    [self addVideo];
494
    //添加音频输入设备
495
    [self addAudio];
496
    //添加预览层
497
    [self addPreviewLayer];
498
    [captureSession commitConfiguration];
499
    //开启会话-->注意,不等于开始录制
500
    [captureSession startRunning];
501
}
502
/*
503
 创建会话,并设置视频分辨率
504
 */
505
- (void)addSession
506
{
507
    captureSession = [[AVCaptureSession alloc] init];
508
    //设置视频分辨率
509
    /*  通常支持如下格式
510
     (
511
     AVAssetExportPresetLowQuality,
512
     AVAssetExportPreset960x540,
513
     AVAssetExportPreset640x480,
514
     AVAssetExportPresetMediumQuality,
515
     AVAssetExportPreset1920x1080,
516
     AVAssetExportPreset1280x720,
517
     AVAssetExportPresetHighestQuality,
518
     AVAssetExportPresetAppleM4A
519
     )
520
     */
521
    //注意,这个地方设置的模式/分辨率大小将影响你后面拍摄照片/视频的大小,
522
    if ([captureSession canSetSessionPreset:AVCaptureSessionPreset352x288]) {
523
        [captureSession setSessionPreset:AVCaptureSessionPreset352x288];
524
    }
525
}
526
/*
527
 添加视频输入设备
528
 */
529
- (void)addVideo
530
{
531
    // 获取摄像头输入设备, 创建 AVCaptureDeviceInput 对象
532
    /* MediaType
533
     AVMediaTypeVideo    //视频
534
     AVMediaTypeAudio    //音频
535
     */
536
    
537
    /* AVCaptureDevicePosition
538
     AVCaptureDevicePositionBack    //后置摄像头
539
     AVCaptureDevicePositionFront   //前置摄像头
540
     */
541
    videoDevice = [self deviceWithMediaType:AVMediaTypeVideo preferringPosition:AVCaptureDevicePositionFront];
542
    
543
    [self addVideoInput];
544
    [self addMovieOutput];
545
}
546
/*
547
 获取摄像头:前置或后置
548
 */
549
- (AVCaptureDevice *)deviceWithMediaType:(NSString *)mediaType preferringPosition:(AVCaptureDevicePosition)position
550
{
551
    NSArray *devices = [AVCaptureDevice devicesWithMediaType:mediaType];
552
    AVCaptureDevice *captureDevice = devices.firstObject;
553
    
554
    for ( AVCaptureDevice *device in devices ) {
555
        if ( device.position == position ) {
556
            captureDevice = device;
557
            break;
558
        }
559
    }
560
    return captureDevice;
561
}
562
/*
563
 添加视频输入对象
564
 */
565
- (void)addVideoInput
566
{
567
    NSError *videoError;
568
    // 根据输入设备初始化输入对象,用户获取输入数据
569
    videoInput = [[AVCaptureDeviceInput alloc] initWithDevice:videoDevice error:&videoError];
570
    if (videoError) {
571
        NSLog(@"---- 取得摄像头设备时出错 ------ %@",videoError);
572
        return;
573
    }
574
    
575
    // 将视频输入对象添加到会话 (AVCaptureSession) 中
576
    if ([captureSession canAddInput:videoInput]) {
577
        [captureSession addInput:videoInput];
578
    }
579
}
580
/*
581
 添加拍摄视频输出对象
582
 */
583
- (void)addMovieOutput
584
{
585
    // 初始化输出设备对象,用户获取输出数据
586
    movieOutput = [[AVCaptureMovieFileOutput alloc] init];
587
    if ([captureSession canAddOutput:movieOutput]) {
588
        [captureSession addOutput:movieOutput];
589
        AVCaptureConnection *captureConnection = [movieOutput connectionWithMediaType:AVMediaTypeVideo];
590
        
591
        //设置视频旋转方向
592
        /*
593
         typedef NS_ENUM(NSInteger, AVCaptureVideoOrientation) {
594
         AVCaptureVideoOrientationPortrait           = 1,
595
         AVCaptureVideoOrientationPortraitUpsideDown = 2,
596
         AVCaptureVideoOrientationLandscapeRight     = 3,
597
         AVCaptureVideoOrientationLandscapeLeft      = 4,
598
         } NS_AVAILABLE(10_7, 4_0) __TVOS_PROHIBITED;
599
         */
600
        //        if ([captureConnection isVideoOrientationSupported]) {
601
        //            [captureConnection setVideoOrientation:AVCaptureVideoOrientationPortrait];
602
        //        }
603
        
604
        // 视频稳定设置
605
        if ([captureConnection isVideoStabilizationSupported]) {
606
            captureConnection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeAuto;
607
        }
608
        captureConnection.videoScaleAndCropFactor = captureConnection.videoMaxScaleAndCropFactor;
609
    }
610
    
611
}
612
/*
613
 添加音频输入设备
614
 */
615
- (void)addAudio
616
{
617
    NSError *audioError;
618
    // 添加一个音频输入设备
619
    audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
620
    // 音频输入对象
621
    audioInput = [[AVCaptureDeviceInput alloc] initWithDevice:audioDevice error:&audioError];
622
    if (audioError) {
623
        NSLog(@"取得录音设备时出错 ------ %@",audioError);
624
        return;
625
    }
626
    // 将音频输入对象添加到会话 (AVCaptureSession) 中
627
    if ([captureSession canAddInput:audioInput]) {
628
        [captureSession addInput:audioInput];
629
    }
630
}
631
/*
632
 创建预览层
633
 */
634
- (void)addPreviewLayer
635
{
636
    [self.view layoutIfNeeded];
637
    
638
    // 通过会话 (AVCaptureSession) 创建预览层
639
    captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:captureSession];
640
    captureVideoPreviewLayer.frame = self.videoView.layer.bounds;
641
    /* 填充模式
642
     AVLayerVideoGravityResize
643
     AVLayerVideoGravityResizeAspect  (default)
644
     AVLayerVideoGravityResizeAspectFill
645
     */
646
    //有时候需要拍摄完整屏幕大小的时候可以修改这个
647
    captureVideoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
648
    // 如果预览图层和视频方向不一致,可以修改这个
649
    captureVideoPreviewLayer.connection.videoOrientation = [movieOutput connectionWithMediaType:AVMediaTypeVideo].videoOrientation;
650
    captureVideoPreviewLayer.position = CGPointMake(self.videoView.width*0.5,self.videoView.height*0.5);
651
    
652
    // 显示在视图表面的图层
653
    CALayer *layer = self.videoView.layer;
654
    layer.masksToBounds = true;
655
    [self.view layoutIfNeeded];
656
    [layer addSublayer:captureVideoPreviewLayer];
657
}
658
659
-(void)pop
660
{
661
    if (self.navigationController) {
662
        [self.navigationController popViewControllerAnimated:YES];
663
    }
664
}
665
666
- (void)showMsgWithTitle:(NSString *)title andContent:(NSString *)content
667
{
668
    [[[UIAlertView alloc] initWithTitle:title message:content delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil] show];
669
}
670
#pragma mark - 点按手势
671
/**
672
 *  添加点按手势,点按时聚焦
673
 */
674
-(void)addGenstureRecognizer{
675
    
676
    UITapGestureRecognizer *singleTapGesture=[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(singleTap:)];
677
    singleTapGesture.numberOfTapsRequired = 1;
678
    singleTapGesture.delaysTouchesBegan = YES;
679
    [self.videoView addGestureRecognizer:singleTapGesture];
680
    
681
    UITapGestureRecognizer *doubleTapGesture=[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(doubleTap:)];
682
    doubleTapGesture.numberOfTapsRequired = 2;
683
    doubleTapGesture.delaysTouchesBegan = YES;
684
    [self.videoView addGestureRecognizer:doubleTapGesture];
685
    
686
    //双击手势执行失败才会执行单击手势
687
    [singleTapGesture requireGestureRecognizerToFail:doubleTapGesture];
688
}
689
/*
690
 单击
691
 */
692
-(void)singleTap:(UITapGestureRecognizer *)tapGesture{
693
    CGPoint point = [tapGesture locationInView:self.videoView];
694
    //将UI坐标转化为摄像头坐标,摄像头聚焦点范围0~1
695
    CGPoint cameraPoint= [captureVideoPreviewLayer captureDevicePointOfInterestForPoint:point];
696
    //    //光圈动画
697
    //    [self setFocusCursorAnimationWithPoint:point];
698
    //更改设备属性
699
    [self changeDevicePropertySafety:^(AVCaptureDevice *captureDevice) {
700
        /*
701
         @constant AVCaptureFocusModeLocked 锁定在当前焦距
702
         @constant AVCaptureFocusModeAutoFocus 自动对焦一次,然后切换到焦距锁定
703
         @constant AVCaptureFocusModeContinuousAutoFocus
704
         */
705
        //当需要时,自动调整焦距
706
        if ([captureDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
707
            [captureDevice setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
708
            NSLog(@"聚焦模式修改为%zd",AVCaptureFocusModeContinuousAutoFocus);
709
        }else{
710
            NSLog(@"聚焦模式修改失败");
711
        }
712
        //聚焦点的位置
713
        if ([captureDevice isFocusPointOfInterestSupported]) {
714
            [captureDevice setFocusPointOfInterest:cameraPoint];
715
        }
716
        /*
717
         @constant AVCaptureExposureModeLocked  曝光锁定在当前值
718
         @constant AVCaptureExposureModeAutoExpose 曝光自动调整一次然后锁定
719
         @constant AVCaptureExposureModeContinuousAutoExposure 曝光自动调整
720
         @constant AVCaptureExposureModeCustom 曝光只根据设定的值来
721
         */
722
        //曝光模式
723
        if ([captureDevice isExposureModeSupported:AVCaptureExposureModeAutoExpose]) {
724
            [captureDevice setExposureMode:AVCaptureExposureModeAutoExpose];
725
        }else{
726
            NSLog(@"曝光模式修改失败");
727
        }
728
        //曝光点的位置
729
        if ([captureDevice isExposurePointOfInterestSupported]) {
730
            [captureDevice setExposurePointOfInterest:cameraPoint];
731
        }
732
    }];
733
}
734
735
/*
736
 更改设备属性
737
 */
738
-(void)changeDevicePropertySafety:(void (^)(AVCaptureDevice *captureDevice))propertyChange{
739
    AVCaptureDevice *captureDevice = [videoInput device];
740
    NSError *error;
741
    //注意改变设备属性前一定要首先调用lockForConfiguration:调用完之后使用unlockForConfiguration方法解锁,意义是---进行修改期间,先锁定,防止多处同时修改
742
    BOOL lockAcquired = [captureDevice lockForConfiguration:&error];
743
    if (!lockAcquired) {
744
        NSLog(@"锁定设备过程error,错误信息:%@",error.localizedDescription);
745
    }else{
746
        [captureSession beginConfiguration];
747
        propertyChange(captureDevice);
748
        [captureDevice unlockForConfiguration];
749
        [captureSession commitConfiguration];
750
    }
751
}
752
/*
753
 双击:设置焦距
754
 */
755
-(void)doubleTap:(UITapGestureRecognizer *)tapGesture{
756
    [self changeDevicePropertySafety:^(AVCaptureDevice *captureDevice) {
757
        if (captureDevice.videoZoomFactor == 1.0) {
758
            CGFloat current = 1.5;
759
            if (current < captureDevice.activeFormat.videoMaxZoomFactor) {
760
                [captureDevice rampToVideoZoomFactor:current withRate:10];
761
            }
762
        }else{
763
            [captureDevice rampToVideoZoomFactor:1.0 withRate:10];
764
        }
765
    }];
766
}
767
768
#pragma mark - other
769
- (void)didReceiveMemoryWarning {
770
    [super didReceiveMemoryWarning];
771
    
772
}
773
774
#pragma mark - 屏幕适配,(320,568)大小为标准
775
+(CGFloat)widthScale:(CGFloat)num{
776
    if (deviceWidth==375) {
777
        return 375*num/320;
778
    }
779
    else if (deviceWidth==414){
780
        return 414*num/320;
781
    }
782
    else{//320
783
        return num;
784
    }
785
}
786
787
+(CGFloat)heightScale:(CGFloat)num{
788
    
789
    if (deviceHeight==480) {
790
        return 480*num/568;
791
    }
792
    else if (deviceHeight==667){
793
        return 667*num/568;
794
    }
795
    else if (deviceHeight==736){//plus
796
        return 736*num/568;
797
    }
798
    else if (deviceHeight==812){//X
799
        return 812*num/568;
800
    }
801
    else{//568
802
        return num;
803
    }
804
}
805
806
@end

+ 20 - 0
WadeMobileFunc/WadeMobileFunc/customVideo/WDFCustomVideo.h

@ -0,0 +1,20 @@
1
//
2
//  WDFCustomVideo.h
3
//  WadeMobileFunc
4
//
5
//  Created by Mac on 2019/11/5.
6
//  Copyright © 2019 asiainfo. All rights reserved.
7
//
8
9
#import <WadeMobile/WadeMobile.h>
10
#import "CustomVideoViewController.h"
11
12
@interface WDFCustomVideo : WDPlugin<CustomVideoViewControllerDelegate>
13
14
/*
15
 录制自定义视频,时长6秒
16
 */
17
-(void)makeShortVideo:(NSArray *)param;
18
19
20
@end

+ 82 - 0
WadeMobileFunc/WadeMobileFunc/customVideo/WDFCustomVideo.m

@ -0,0 +1,82 @@
1
//
2
//  WDFCustomVideo.m
3
//  WadeMobileFunc
4
//
5
//  Created by Mac on 2019/11/5.
6
//  Copyright © 2019 asiainfo. All rights reserved.
7
//
8
9
#import "WDFCustomVideo.h"
10
#import <WadeMobile/WDCommonTool.h>
11
12
#define deviceHeight ([[UIScreen mainScreen] bounds].size.height)
13
#define deviceWidth ([[UIScreen mainScreen] bounds].size.width)
14
#define isIphoneX ([[UIScreen mainScreen] bounds].size.height==812?YES:NO)
15
#define widthScale(f) [CustomVideoViewController widthScale:f]
16
#define heightScale(f) [CustomVideoViewController heightScale:f]
17
18
@implementation WDFCustomVideo
19
20
/*
21
 录制自定义视频,时长6秒
22
 */
23
-(void)makeShortVideo:(NSArray *)param{
24
    int seconds = 6;
25
    if (param && param.count>0) {
26
        NSString *param0 = [NSString stringWithFormat:@"%@",param[0]];
27
        seconds = param0.intValue;
28
    }
29
    CustomVideoViewController *videoVC = [[CustomVideoViewController alloc] initVCWithRecordSeconds:seconds];
30
    videoVC.delegate = self;
31
    UINavigationController *nc = [[UINavigationController alloc] initWithRootViewController:videoVC];
32
    [[self getViewController] presentViewController:nc animated: YES completion: nil];
33
}
34
35
#pragma mark - CustomVideoViewControllerDelegate
36
/*
37
 自定义录制界面视图
38
 */
39
-(void)customWithBaseView:(UIView *)baseView VideoView:(UIView *)videoView SourceBundle:(NSBundle *)bundle{
40
    //验证码
41
    UIView *topBaseView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, deviceWidth, 100)];
42
    topBaseView.backgroundColor = [UIColor whiteColor];
43
    [baseView addSubview:topBaseView];
44
    
45
    UILabel *lbl1 = [[UILabel alloc] initWithFrame:CGRectMake(0, 10, deviceWidth, 20)];
46
    lbl1.font = [UIFont systemFontOfSize:12];
47
    lbl1.attributedText = [WDCommonTool fetchAttributedText1:@"请使用" Text2:@"前置摄像头" Text3:@",录制" Text4:@"6秒" Text5:@"视频" Color1:[WDCommonTool convertHexToColor:@"#1c1b21"] Color2:[WDCommonTool convertHexToColor:@"#dd4b4b"] Color3:[WDCommonTool convertHexToColor:@"#1c1b21"] Color4:[WDCommonTool convertHexToColor:@"#dd4b4b"] Color5:[WDCommonTool convertHexToColor:@"#1c1b21"]];
48
    lbl1.textAlignment = NSTextAlignmentCenter;
49
    [topBaseView addSubview:lbl1];
50
    
51
    UILabel *lbl2 = [[UILabel alloc] initWithFrame:CGRectMake(0, lbl1.bottom, deviceWidth, 20)];
52
    lbl2.font = [UIFont systemFontOfSize:12];
53
    lbl2.attributedText = [WDCommonTool fetchAttributedText1:@"录制时请" Text2:@"匀速朗读" Text3:@"一遍下方数字" Color1:[WDCommonTool convertHexToColor:@"#1c1b21"] Color2:[WDCommonTool convertHexToColor:@"#dd4b4b"] Color3:[WDCommonTool convertHexToColor:@"#1c1b21"]];
54
    lbl2.textAlignment = NSTextAlignmentCenter;
55
    [topBaseView addSubview:lbl2];
56
    
57
    UILabel *codeLbl = [[UILabel alloc] initWithFrame:CGRectMake(0, lbl2.bottom, deviceWidth, 50)];
58
    codeLbl.font = [UIFont boldSystemFontOfSize:30];
59
    codeLbl.textColor = [WDCommonTool convertHexToColor:@"#dd4b4b"];
60
    NSDictionary *dic = @{NSKernAttributeName:@5.0f};
61
    NSAttributedString *attributeStr = [[NSAttributedString alloc] initWithString:@"9876" attributes:dic];
62
    codeLbl.attributedText = attributeStr;
63
    codeLbl.textAlignment = NSTextAlignmentCenter;
64
    [topBaseView addSubview:codeLbl];
65
    
66
    UIImage *peopleImg = [UIImage imageNamed:@"people" inBundle:bundle compatibleWithTraitCollection:nil];
67
    UIImageView *rectImage = [[UIImageView alloc] initWithImage:peopleImg];
68
    if (isIphoneX) {
69
        rectImage.frame = CGRectMake(0, 150, deviceWidth, 330);
70
    }else{
71
        rectImage.frame = CGRectMake(0, 120, deviceWidth, videoView.height-270);
72
    }
73
    [baseView addSubview:rectImage];
74
}
75
/*
76
 录制好的视频处理,或上传,或保存至相册
77
 */
78
-(void)confirmWithData:(NSData *)data FilePath:(NSString *)filepath{
79
    [self callback:filepath];
80
}
81
82
@end