iOS录屏代码

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface RecordManager : NSObject

@property (nonatomic, weak) UIViewController *baseVC;

- (void)startRecordWithForceRecord:(BOOL)forceRecord;
- (void)stopRecord;

@end

NS_ASSUME_NONNULL_END

#import "RecordManager.h"
#import <ReplayKit/ReplayKit.h>
#import <Photos/Photos.h>

@interface RecordManager () <RPPreviewViewControllerDelegate>

@property (strong, nonatomic) RPScreenRecorder *screenRecorder;
@property (strong, nonatomic) AVAssetWriter *assetWriter;
@property (strong, nonatomic) AVAssetWriterInput *videoInput;
@property (strong, nonatomic) AVAssetWriterInput *audioInput;
@property (assign, nonatomic) BOOL forceRecord;
@end

@implementation RecordManager

- (RPScreenRecorder *)screenRecorder {
    if (!_screenRecorder) {
        _screenRecorder = [RPScreenRecorder sharedRecorder];
    }
    return _screenRecorder;
}

- (void)startRecordWithForceRecord:(BOOL)forceRecord {
    
    NSLog(@"ReplayKit只支持真机录屏,支持游戏录屏,不支持录avplayer播放的视频");
    NSLog(@"检查机器和版本是否支持ReplayKit录制...");
    if ([self.screenRecorder isAvailable]) {
        NSLog(@"支持ReplayKit录制");
    } else {
        NSLog(@"!!不支持支持ReplayKit录制!!");
        return;
    }
    
    if (self.screenRecorder.isRecording) {
        NSLog(@"stop first");
        return;
    }
    _forceRecord = forceRecord;
    NSLog(@"开始录制");
    
    if (@available(iOS 11.0, *) && !forceRecord) {
        [self startCapture];
    } else if (@available(iOS 10.0, *)) {
    
        //在此可以设置是否允许麦克风(传YES即是使用麦克风,传NO则不是用麦克风)
        self.screenRecorder.microphoneEnabled = YES;
        [self.screenRecorder startRecordingWithHandler:^(NSError * _Nullable error) {
            NSLog(@"录制开始...");
            if (error) {
                NSLog(@"错误信息 %@", error);
            } else {
            }
        }];
    } else {
        //在此可以设置是否允许麦克风(传YES即是使用麦克风,传NO则不是用麦克风)
        [self.screenRecorder startRecordingWithMicrophoneEnabled:YES handler:^(NSError *error){
            NSLog(@"录制开始...");
            if (error) {
                NSLog(@"错误信息 %@", error);
            } else {
            }
        }];
    }
    

}

- (void)stopRecord {
    NSLog(@"结束录制");
    if (!self.screenRecorder.isRecording) {
        return;
    }
    
    if (@available(iOS 11.0, *) && !_forceRecord) {
        [self stopCapture];
    } else {
        [self.screenRecorder stopRecordingWithHandler:^(RPPreviewViewController *previewViewController, NSError *  error){


            if (error) {
                NSLog(@"失败消息:%@", error);
            } else {
                NSLog(@"录制完成");
                
                if (@available(iOS 10.0, *)) {
                    
                    /*
                     iOS 10 这里我选择直接保存到相册,不使用 previewViewController 弹出来
                     */
                    [self handleiOS10:previewViewController];
                    
                } else if (@available(iOS 9.0, *)) {
                    
                    /*
                     在iOS9 里面取不到 movieURL 这个变量,程序会崩溃. 所以直接弹出这个 previewViewController 让用户来操作保存
                     */
                    [self handleiOS9:previewViewController];
                }

            }
        }];
    }
    

}

- (void)startCapture {
    if (self.screenRecorder.isRecording) {
        return;
    }
    NSError *error = nil;
    NSString *outputURL = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
    NSString *fileName = [NSString stringWithFormat:@"Capture%@.mp4", [NSDate date]];
    NSString *videoOutPath = [outputURL stringByAppendingPathComponent:fileName];
    self.assetWriter = [AVAssetWriter assetWriterWithURL:[NSURL fileURLWithPath:videoOutPath] fileType:AVFileTypeMPEG4 error:&error];

    
    
    if (@available(iOS 11.0, *)) {
        NSDictionary *compressionProperties = @{AVVideoProfileLevelKey         : AVVideoProfileLevelH264HighAutoLevel,
                                                AVVideoH264EntropyModeKey      : AVVideoH264EntropyModeCABAC,
                                                AVVideoAverageBitRateKey       : @(1000000),
                                                AVVideoMaxKeyFrameIntervalKey  : @1000,
                                                AVVideoAllowFrameReorderingKey : @NO};
        NSNumber* width= @1920;//@([UIScreen mainScreen].bounds.size.width);
        NSNumber* height = @1440;//@([UIScreen mainScreen].bounds.size.height);
        NSDictionary *videoSettings = @{AVVideoCompressionPropertiesKey : compressionProperties,
                                        AVVideoCodecKey                 : AVVideoCodecTypeH264,
                                        AVVideoWidthKey                 : width,
                                        AVVideoHeightKey                : height};

        self.videoInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoSettings];
        AudioChannelLayout acl;
        bzero(&acl, sizeof(acl));
        acl.mChannelLayoutTag = kAudioChannelLayoutTag_Mono;
        NSDictionary *audioSettings = @{AVFormatIDKey : [ NSNumber numberWithInt: kAudioFormatMPEG4AAC],
                                        AVNumberOfChannelsKey : @1,
                                        AVSampleRateKey                 : @16000,
                                        AVChannelLayoutKey                : [ NSData dataWithBytes: &acl length: sizeof( AudioChannelLayout ) ]};

        self.audioInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio outputSettings:audioSettings];
    } else {
        // Fallback on earlier versions
    }

    if([self.assetWriter canAddInput:self.videoInput]) {
        [self.assetWriter addInput:self.videoInput];
    }
    if([self.assetWriter canAddInput:self.audioInput]) {
        [self.assetWriter addInput:self.audioInput];
    }
    
//    [self.assetWriter addInput:self.audioInput];
    [self.videoInput setMediaTimeScale:60];
    [self.assetWriter setMovieTimeScale:60];
    [self.videoInput setExpectsMediaDataInRealTime:YES];

    if (@available(iOS 11.0, *)) {
        [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
            dispatch_async(dispatch_get_main_queue(), ^{
                if (granted)
                {
                    [self.screenRecorder setMicrophoneEnabled:YES];
                    __block BOOL start = NO;
                    [self.assetWriter startWriting];
//                    [self.assetWriter startSessionAtSourceTime:CMSampleBufferGetPresentationTimeStamp(sampleBuffer)];
                    [self.screenRecorder startCaptureWithHandler:^(CMSampleBufferRef  _Nonnull sampleBuffer, RPSampleBufferType bufferType, NSError * _Nullable error) {
                        if (CMSampleBufferDataIsReady(sampleBuffer)) {
                            if (!start) {
                                start = YES;
                                [self.assetWriter startSessionAtSourceTime:CMSampleBufferGetPresentationTimeStamp(sampleBuffer)];
                            }

                            if (self.assetWriter.status == AVAssetWriterStatusFailed) {
                                NSLog(@"An error occured.");
                                //show alert
                                [self.screenRecorder stopCaptureWithHandler:^(NSError * _Nullable error) {}];
                                return;
                            }
                            if (bufferType == RPSampleBufferTypeVideo) {
                                if (self.videoInput.isReadyForMoreMediaData) {
                                    [self.videoInput appendSampleBuffer:sampleBuffer];
                                }else{
                                    NSLog(@"Not ready for video");
                                }
                            }else if(bufferType == RPSampleBufferTypeAudioMic){
                                if (self.audioInput.isReadyForMoreMediaData) {
                                   [self.audioInput appendSampleBuffer:sampleBuffer];
                                }else{
                                   NSLog(@"Not ready for audio");
                                }
                            }
                        }
                    } completionHandler:^(NSError * _Nullable error) {
                        if (!error) {
                            AVAudioSession *session = [AVAudioSession sharedInstance];
                            [session setActive:YES error:nil];
                            // Start recording
                            NSLog(@"Recording started successfully.");
                        }else{
                            //show alert
                        }
                    }];
                }
            });
        }];


    } else {
        // Fallback on earlier versions
    }

}


- (void)stopCapture {

    if (@available(iOS 11.0, *)) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.screenRecorder stopCaptureWithHandler:^(NSError * _Nullable error) {
                if (!error) {
                    NSLog(@"Recording stopped successfully. Cleaning up...");
                    [self.videoInput markAsFinished];
                    [self.audioInput markAsFinished];
                    [self.assetWriter finishWritingWithCompletionHandler:^{
                        NSLog(@"File Url:  %@",self.assetWriter.outputURL);
                        self.videoInput = nil;
                        self.audioInput = nil;
                        self.assetWriter = nil;
                        self.screenRecorder = nil;
                    }];
                }
            }];
        });


    } else {
        // Fallback on earlier versions
        NSLog(@"hello");
    }
}

/// 处理 iOS 10
- (void)handleiOS10:(RPPreviewViewController *)previewViewController {
    
    NSURL *videoURL = [previewViewController valueForKey:@"movieURL"];
    NSLog(@"在previewViewController找到的视频路径 %@", videoURL.absoluteString);
    if (videoURL) {
        BOOL compatible = UIVideoAtPathIsCompatibleWithSavedPhotosAlbum([videoURL path]);
        
        if (compatible) {
            UISaveVideoAtPathToSavedPhotosAlbum([videoURL path], self, @selector(savedPhotoImage:didFinishSavingWithError:contextInfo:), nil);
        }
        
    } else {
        NSLog(@"没有找到 movieURL");
    }
}


#pragma mark -  处理 iOS 9
- (void)handleiOS9:(RPPreviewViewController *)previewViewController {
    previewViewController.previewControllerDelegate = self;
    [self.baseVC presentViewController:previewViewController animated:YES completion:nil];
}

// 视频预览页面 回调
//关闭的回调
- (void)previewControllerDidFinish:(RPPreviewViewController *)previewController {
    [previewController dismissViewControllerAnimated:YES completion:nil];
}

//选择了某些功能的回调(如分享和保存)
- (void)previewController:(RPPreviewViewController *)previewController didFinishWithActivityTypes:(NSSet <NSString *> *)activityTypes {

    if ([activityTypes containsObject:@"com.apple.UIKit.activity.SaveToCameraRoll"]) {

        NSLog(@"已经保存到系统相册");
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [self saveVideoToDocument];
        });
    }
    if ([activityTypes containsObject:@"com.apple.UIKit.activity.CopyToPasteboard"]) {
        NSLog(@"复制成功");
    }
}

#pragma mark - iOS 10 保存视频的回调

//保存视频完成之后的回调
- (void)savedPhotoImage:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo {
    
    if (error) {
        NSLog(@"保存视频失败 == %@", error.description);
    } else {
        //取出这个视频并按创建日期排序
        [self saveVideoToDocument];
    }
}

//保存视频至沙盒
- (void)saveVideoToDocument {
    //取出这个视频并按创建日期排序
    PHFetchOptions * options = [[PHFetchOptions alloc] init];
    options.sortDescriptors  = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
    PHFetchResult * assetsFetchResults = [PHAsset fetchAssetsWithOptions:options];
    PHAsset * phasset = [assetsFetchResults lastObject];
    if (phasset) {
        //视频文件
        if (phasset.mediaType == PHAssetMediaTypeVideo) {
            PHImageManager * manager = [PHImageManager defaultManager];
            [manager requestAVAssetForVideo:phasset options:nil resultHandler:^(AVAsset * _Nullable asset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) {
                AVURLAsset * urlAsset = (AVURLAsset *)asset;
                NSString *fileName = [NSString stringWithFormat:@"Record%@.mp4", [NSDate date]];
                NSString * outPath  = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:fileName];
                NSURL *videoURL = urlAsset.URL;
                NSLog(@"在相册找到的视频路径 %@", videoURL.absoluteString);
                [self extractVideoFromAlbum:videoURL outputURL:[NSURL fileURLWithPath:outPath] blockHandler:^(AVAssetExportSession * _Nonnull session) {
                    if (session.status == AVAssetExportSessionStatusCompleted) {
                        NSLog(@"视频已处理好可以对其进行操作");
                        //处理完的视频是否需要删除?自行决定
                    } else {
                        NSLog(@"视频压缩出错...");
                    }
                }];
            }];
        } else {
            NSLog(@"未成功保存视频...");
        }
    } else {
        NSLog(@"未成功保存视频...");
    }
    
}

- (void)extractVideoFromAlbum:(NSURL *)inputURL
                             outputURL:(NSURL *)outputURL
                          blockHandler:(void (^)(AVAssetExportSession * _Nonnull))handler {
    
    AVURLAsset * asset = [AVURLAsset URLAssetWithURL:inputURL options:nil];
    AVAssetExportSession * session = [[AVAssetExportSession alloc] initWithAsset:asset presetName:AVAssetExportPresetHighestQuality];
    session.outputURL = outputURL;
    session.outputFileType = AVFileTypeMPEG4;
    [session exportAsynchronouslyWithCompletionHandler:^(void) {
        if (handler) {
            handler(session);
        }
    }];
}





@end