iOS 自定义视频拍摄开发(GinCamera/GinVideoCaptureManager)

iOS 自定义视频拍摄开发(GinCamera/GinVideoCaptureManager)

先附上DEMO
[GinCamera] (https://github.com/ginhoor/GinCamera)

视频拍摄和照片拍摄差不多,只是数据方面多了一个音频。

同样具体的详细创建过程请直接下载demo查阅


主要对象

@property (strong, nonatomic) AVCaptureSession *captureSession;
/**
 视频设备
 */
@property (strong, nonatomic) AVCaptureDevice *captureDevice;
@property (strong, nonatomic) AVCaptureDeviceInput *captureDeviceInput;
/**
 音频设备
 */
@property (strong, nonatomic) AVCaptureDevice *audioDevice;
@property (strong, nonatomic) AVCaptureDeviceInput *audioDeviceInput;
/**
 视频输出流
 */
@property (strong, nonatomic) AVCaptureMovieFileOutput *captureMovieFileOutput;
/**
 预览层
 */
@property (strong, nonatomic) AVCaptureVideoPreviewLayer *captureVideoPreviewLayer;

视频拍摄与拍照不同,需要先设定视频的存放地址

NSURL *fileUrl = [NSURL fileURLWithPath:filePath];
self.videoFilePath = filePath;
[self.captureMovieFileOutput startRecordingToOutputFileURL:fileUrl recordingDelegate:self];

视频拍摄需要同时获得摄像头权限与麦克风权限,常见到有人问为什么开了摄像头权限,但是取景画面不出来,这个时候请检查麦克风权限。

开启视频防抖功能

        AVCaptureConnection *captureConnection = [self.captureMovieFileOutput connectionWithMediaType:AVMediaTypeVideo];
        // 开启视频防抖
        if ([captureConnection isVideoStabilizationSupported]) {
            captureConnection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeAuto;
        }

检测视频区域发生动态变化

通过监听AVCaptureDeviceSubjectAreaDidChangeNotification通知,可以获得回调
视频合成方法

获得指定视频的预览图

/**
 获得指定视频的预览图

 @param filePath 视频地址
 @return 预览图
 */+ (UIImage *)getVideoPreViewImage:(NSString *)filePath
{
    AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:[NSURL fileURLWithPath:filePath] options:nil];
    
    AVAssetImageGenerator *generator = [[AVAssetImageGenerator alloc] initWithAsset:asset];
    generator.appliesPreferredTrackTransform = YES;
    CMTime time = CMTimeMakeWithSeconds(0.0, 600);
    CMTime actualTime;
    NSError *error = nil;
    CGImageRef image = [generator copyCGImageAtTime:time actualTime:&actualTime error:&error];
    if (error) {
        NSLog(@"get preview image failed!! error:%@",error);
        CGImageRelease(image);
        return nil;
    }
    UIImage *videoImage = [[UIImage alloc] initWithCGImage:image];
    CGImageRelease(image);
    return videoImage;
}

多个视频合成

/**
 视频合成

 @param videoFilePathList 视频地址
 @param outputPath 输出地址
 @param presetName 分辨率,默认:AVAssetExportPreset640x480
 @param outputFileType 输出格式,默认:AVFileTypeMPEG4
 @param completion 完成回调
 */

+ (void)mergeAndExportVideos:(NSArray <NSString *> *)videoFilePathList
                  outputPath:(NSString *)outputPath
                  presetName:(NSString *)presetName
              outputFileType:(NSString *)outputFileType
                  completion:(void(^)(BOOL success))completion
{
    if (videoFilePathList.count == 0) {
        return;
    }

    NSLog(@"videoFilePathList--->%@",videoFilePathList);
    
    AVMutableComposition *mixComposition = [AVMutableComposition composition];
    
    AVMutableCompositionTrack *audioTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
    
    AVMutableCompositionTrack *videoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
    
    __block CMTime totalDuration = kCMTimeZero;
    
    [videoFilePathList enumerateObjectsUsingBlock:^(NSString * _Nonnull filePath, NSUInteger idx, BOOL * _Nonnull stop) {
        
        AVURLAsset *asset = [AVURLAsset assetWithURL:[NSURL fileURLWithPath:filePath]];
        
        NSError *audioError = nil;
        // 获取AVAsset中的音频
        AVAssetTrack *assetAudioTrack = [[asset tracksWithMediaType:AVMediaTypeAudio] firstObject];
        // 向通道内加入音频
        BOOL audioFlag = [audioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration)
                                             ofTrack:assetAudioTrack
                                              atTime:totalDuration
                                               error:&audioError];
        if (!audioFlag) {
            NSLog(@"audioTrack insert error:%@,%d",audioError,audioFlag);
        }
        
        // 向通道内加入视频
        NSError *videoError = nil;
        AVAssetTrack *assetVideoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] firstObject];
        [videoTrack setPreferredTransform:assetVideoTrack.preferredTransform];
        
        BOOL videoFlag = [videoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration)
                                             ofTrack:assetVideoTrack
                                              atTime:totalDuration
                                               error:&videoError];
        
        if (!videoFlag) {
            NSLog(@"videoTrack insert error:%@,%d",videoError,videoFlag);
        }
        totalDuration = CMTimeAdd(totalDuration, asset.duration);
    }];

    NSURL *mergeFileURL = [NSURL fileURLWithPath:outputPath];
    // 分辨率
    if (![presetName isNotBlank]) {
        presetName = AVAssetExportPreset640x480;
    }
    AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:presetName];
    exporter.outputURL = mergeFileURL;
    // 转出格式
    if (![outputFileType isNotBlank]) {
        outputFileType = AVFileTypeMPEG4;
    }
    exporter.outputFileType = outputFileType;
    exporter.shouldOptimizeForNetworkUse = YES;
    [exporter exportAsynchronouslyWithCompletionHandler:^{
        if (exporter.error) {
            NSLog(@"AVAssetExportSession Error:%@",exporter.error);
            if (completion) {
                completion(NO);
            }
            return;
        }
        // 导出状态为完成
        if ([exporter status] == AVAssetExportSessionStatusCompleted) {
            NSLog(@"视频合并完成--->%@",mergeFileURL);

            if (completion) {
                completion(YES);
            }
        } else {
            NSLog(@"AVAssetExportSession 当前压缩进度:%f",exporter.progress);
        }
    }];
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值