欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

iOS 录制屏幕

程序员文章站 2022-05-16 14:35:21
...

思路1:http://blog.sina.com.cn/s/blog_6d64a8df01018i5n.html

思路总结:http://www.jianshu.com/p/be8040eb4106

录制屏幕操作,同时录制声音,合成视频存入相册:
https://github.com/Blazeice/ScreenAndAudioRecordDemoScreenAndAudioRecordDemo

本人在项目中先用ReplayKit实现了录屏幕逻辑,根据https://github.com/cdpenggod/CDPReplay
项目改动而来,多谢作者
录制代码奉上

//
//  ULCDPReplay.h
//  UXLive
//
//  Created by zhangxiwei on 2017/6/20.
//  Copyright © 2017年 UXIN CO. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <AssetsLibrary/ALAssetsLibrary.h>
#import <Photos/Photos.h>
#import <UIKit/UIKit.h>
#import <ReplayKit/ReplayKit.h>

typedef void(^ULReplayPreviewBlock)(BOOL isSuccess,RPPreviewViewController *viewController);

/**
 录制回调delegate
 */
@protocol ULCDPReplayDelegate <NSObject>
@optional

/**
 *  开始录制回调
 */
- (void)replayRecordStart;


/**
 录制结束或错误回调

 @param previewViewController 预览viewController
 @param errorInfo 错误信息
 */
- (void)replayRecordFinishWithVC:(RPPreviewViewController *)previewViewController errorInfo:(NSString *)errorInfo;

/**
 *  保存到系统相册成功回调
 */
- (void)saveSuccess;


@end


/**
 系统版本需要是iOS9.0及以上才支持ReplayKit框架录制
 */
@interface ULCDPReplay : NSObject

/**
 *  代理
 */
@property (nonatomic,weak) id <ULCDPReplayDelegate> delegate;

/**
 *  是否正在录制
 */
@property (nonatomic,assign,readonly) BOOL isRecording;

/**
 *  单例对象
 */
+ (instancetype)sharedReplay;


/**
 开始录制

 @param block 回调
 */
- (void)startRecordBlock:(void(^)(BOOL successed, NSString *msg))block;

/**
 结束录制
 @param isShow isShow   是否录制完后自动展示视频预览页
 @param finishBlock     完成回调
 @param previewBlock    预览回调
 */
- (void)stopRecordAndShowVideoPreviewController:(BOOL)isShow
                                   finishBlock:(void(^)(BOOL successed, NSURL *videoUrl ,NSString *msg))finishBlock
                                  previewBlock:(ULReplayPreviewBlock)previewBlock;

/**
 取消录制
 */
- (void)cancelRecord;

@end
//
//  ULCDPReplay.m
//  UXLive
//
//  Created by zhangxiwei on 2017/6/20.
//  Copyright © 2017年 UXIN CO. All rights reserved.
//
#import "ULCDPReplay.h"
#import "ULAssertUtil.h"

@interface ULCDPReplay () <RPPreviewViewControllerDelegate>

@property (nonatomic, strong) NSDate *createDate;

@end

@implementation ULCDPReplay

/**
 单例化对象

 @return 实例
 */
+ (instancetype)sharedReplay {
    static ULCDPReplay *replay=nil;
    static dispatch_once_t token;
    dispatch_once(&token, ^{
        replay=[[ULCDPReplay alloc] init];
    });
    return replay;
}

/**
 是否正在录制

 @return 是否
 */
- (BOOL)isRecording {
    return [RPScreenRecorder sharedRecorder].recording;
}

#pragma mark - 开始/结束录制

- (void)startRecordBlock:(void (^)(BOOL success, NSString *message))block {
    self.createDate = [NSDate date];
    if ([RPScreenRecorder sharedRecorder].recording==YES) {
        ULLogInfo(@"CDPReplay:已经开始录制");
        return;
    }
    if ([self systemVersionOK]) {
        if ([[RPScreenRecorder sharedRecorder] isAvailable]) {
            ULLogInfo(@"CDPReplay:录制开始初始化");
            [ULToast showProgress:@"录屏初始化中..."];
            [[RPScreenRecorder sharedRecorder] startRecordingWithMicrophoneEnabled:YES handler:^(NSError *error){
                dispatch_main_sync_ulsafe(^{
                    [ULToast hideProgress];
                });
                if (error) {
                    ULLogInfo(@"CDPReplay:开始录制error %@",error);
                    [[RPScreenRecorder sharedRecorder] discardRecordingWithHandler:^{
                        ULLogInfo(@"CDPReplay:discardRecordingWithHandler");
                    }];
                    if (block) {
                        block(NO, @"录制失败,请重试");
                    }
                    if ([_delegate respondsToSelector:@selector(replayRecordFinishWithVC:errorInfo:)]) {
                        [_delegate replayRecordFinishWithVC:nil errorInfo:[NSString stringWithFormat:@"CDPReplay:开始录制error %@",error]];
                    }
                } else {
                    ULLogInfo(@"CDPReplay:开始录制");
                    if (block) {
                        block(YES, @"开始录制");
                    }
                    if ([_delegate respondsToSelector:@selector(replayRecordStart)]) {
                        [_delegate replayRecordStart];
                    }
                }

            }];
        } else {
            if (block) {
                block(NO, @"环境不支持屏幕录制");
            }
            ULLogInfo(@"CDPReplay:环境不支持ReplayKit录制");
            if ([_delegate respondsToSelector:@selector(replayRecordFinishWithVC:errorInfo:)]) {
                [_delegate replayRecordFinishWithVC:nil errorInfo:@"CDPReplay:环境不支持ReplayKit录制"];
            }
        }
    } else {
        ULLogInfo(@"CDPReplay:系统版本需要是iOS9.0及以上才支持ReplayKit录制");
        if (block) {
            block(NO, @"系统版本需要是iOS9.0及以上才支持屏幕录制");
        }
        if ([_delegate respondsToSelector:@selector(replayRecordFinishWithVC:errorInfo:)]) {
            [_delegate replayRecordFinishWithVC:nil errorInfo:@"CDPReplay:系统版本需要是iOS9.0及以上才支持ReplayKit录制"];
        }
    }
}

- (void)stopRecordAndShowVideoPreviewController:(BOOL)isShow
                                    finishBlock:(void (^)(BOOL, NSURL *, NSString *))finishBlock
                                   previewBlock:(ULReplayPreviewBlock)previewBlock {
    ULLogInfo(@"CDPReplay:正在结束录制");
    WS(weakSelf)
    [[RPScreenRecorder sharedRecorder] stopRecordingWithHandler:^(RPPreviewViewController *previewViewController, NSError *  error){
        if (error) {
            ULLogInfo(@"CDPReplay:结束录制error %@", error);
            [[RPScreenRecorder sharedRecorder] discardRecordingWithHandler:^{
                ULLogInfo(@"CDPReplay:discardRecordingWithHandler");
            }];
            if ([_delegate respondsToSelector:@selector(replayRecordFinishWithVC:errorInfo:)]) {
                [_delegate replayRecordFinishWithVC:nil errorInfo:[NSString stringWithFormat:@"CDPReplay:结束录制error %@",error]];
            }
            if (finishBlock) {
                finishBlock(NO, nil, @"录制失败,请稍后重试");
            }
        } else {
            ULLogInfo(@"CDPReplay:录制完成");
            if ([_delegate respondsToSelector:@selector(replayRecordFinishWithVC:errorInfo:)]) {
                [_delegate replayRecordFinishWithVC:previewViewController errorInfo:@""];
            }
            [previewViewController setPreviewControllerDelegate:weakSelf];
            if (ULOSVersionFloat >= 9.0f && ULOSVersionFloat < 10.0f) {
                if (previewBlock) {
                    previewBlock(YES, previewViewController);
                }
            } else {
                NSURL *movieURL = [previewViewController valueForKey:@"movieURL"];

                ALAssetsLibrary *assetsLibrary = [[ALAssetsLibrary alloc]init];
                [assetsLibrary writeVideoAtPathToSavedPhotosAlbum:movieURL completionBlock:^(NSURL *assetURL, NSError *error) {
                    if (error) {
                        ULLogInfo(@"CDPReplay 保存失败 %@ %@", error, assetURL);
                        if (finishBlock) {
                            finishBlock(NO, assetURL, @"录制成功");
                        }
                    } else {
                        ULLogInfo(@"CDPReplay 保存成功 %@", assetURL);
                        if (finishBlock) {
                            finishBlock(YES, assetURL, @"录制成功");
                        }
                    }
                }];
            }
        }
    }];
}

- (void)cancelRecord {
    ULLogInfo(@"CDPReplay 取消录制");
    [[RPScreenRecorder sharedRecorder] stopRecordingWithHandler:^(RPPreviewViewController *previewViewController, NSError *  error){
        if (error) {
            ULLogInfo(@"CDPReplay cancelRecord:结束录制error %@", error);
        } else {
            ULLogInfo(@"CDPReplay cancelRecord:录制完成");
        }
        [[RPScreenRecorder sharedRecorder] discardRecordingWithHandler:^{

        }];
    }];
}

#pragma mark - 其他方法

/**
 判断对应系统版本是否支持ReplayKit

 @return 是否支持系统
 */
- (BOOL)systemVersionOK {
    if (IS_IOS9_OR_LATER) {
        return YES;
    } else {
        return NO;
    }
}


@end

注意点:

  • 系统版本在iOS10 以上
NSURL *movieURL = [previewViewController valueForKey:@"movieURL"];

通过私有变量能获取到存储地址,可以直接读取视频地址了,但是存在几个坑,各位有好办法也可以同步,我直接拿地址操作时不行的,以为地址的目录是在ReplayKit的一个目录下,个人理解应该是跨应用了,数据保护之类的(猜测),我做了一把直接存储到相册,然后再从相册里面读取就能对文件进行操作了。

  • 系统 iOS9~iOS 10包含iOS9

该系统下通过previewViewController通过上述方法是找不到的,打印一下私有属性就能证明,只能通过提供的previewViewController来保存做操作。我的方法是引导用户去点击存储,然后通过点击存储回调从相册里面获取最新的文件,(坑出现)虽然给的回调是存储回调,但是一般情况下这个时候文件还没有存储成功,我延迟1s后获取。因为我们一般都是限制60s以内的视频,所以一般都没有问题。

获取最新的asset

+ (PHAsset *)latestAsset {
    // 获取所有资源的集合,并按资源的创建时间排序
    PHFetchOptions *options = [[PHFetchOptions alloc] init];
    options.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:NO]];
    options.predicate = [NSPredicate predicateWithFormat:@"mediaType == %ld",
                        PHAssetMediaTypeVideo];
    PHFetchResult *assetsFetchResults = [PHAsset fetchAssetsWithOptions:options];
    return [assetsFetchResults firstObject];
}