工程中用到了琅束,所以小研究了下
一.使用條件
(1)系統(tǒng)版本需要是iOS9.0及以上才支持
(2)模擬器不支持錄屏
(3)[RPScreenRecorder sharedRecorder].available 框架自帶的檢測(cè)機(jī)器硬件等是否支持
二.錄屏方法
1.開(kāi)始錄制
(1)- (void)startRecordingWithMicrophoneEnabled:(BOOL)microphoneEnabled handler:(nullable void(^)(NSError * _Nullable error))handler可以選擇是否開(kāi)啟麥克風(fēng)
(2)- (void)startRecordingWithHandler:(nullable void(^)(NSError * _Nullable error))handler
(3)- (void)startCaptureWithHandler:(nullable void(^)(CMSampleBufferRef sampleBuffer, RPSampleBufferType bufferType, NSError * _Nullable error))captureHandler completionHandler:(nullable void(^)(NSError * _Nullable error))completionHandler iOS才能用的方法茂契,錄制屏幕的時(shí)候可以獲取到流媒體(得到的流媒體可以自己去處理分辨率和格式迫肖,也就是說(shuō)可以直接將流媒體直播出去漩绵,不需要去選擇第三方的直播軟件(前提是你的app有直播功能))sampleBuffer的默認(rèn)格式是420f
2.結(jié)束錄制
(1)- (void)stopRecordingWithHandler:(nullable void(^)(RPPreviewViewController * _Nullable previewViewController, NSError * _Nullable error))handler 與開(kāi)始錄制的(1)(2)對(duì)應(yīng)
previewViewController 是顯示你錄制的內(nèi)容的容器跟磨,錄制結(jié)束需要顯示的錄制內(nèi)容需要如下設(shè)置摩泪,設(shè)置代理可以監(jiān)控保存吼鱼,分享躬贡,返回等
if (previewViewController) {
//設(shè)置預(yù)覽頁(yè)面到代理
previewViewController.previewControllerDelegate = self;
[self presentViewController:previewViewController animated:YES completion:nil];
}
(2)- (void)stopCaptureWithHandler:(nullable void(^)(NSError * _Nullable error))handler與開(kāi)始錄制的(3)對(duì)應(yīng)
3.RPScreenRecorderDelegate
(1)- (void)screenRecorder:(RPScreenRecorder *)screenRecorder didStopRecordingWithError:(NSError *)error previewViewController:(nullable RPPreviewViewController *)previewViewController API_DEPRECATED("No longer supported"
(2)- (void)screenRecorder:(RPScreenRecorder *)screenRecorder didStopRecordingWithPreviewViewController:(nullable RPPreviewViewController *)previewViewController error:(nullable NSError *)error
(3)- (void)screenRecorderDidChangeAvailability:(RPScreenRecorder *)screenRecorder;用來(lái)監(jiān)聽(tīng) ReplayKit 是否可用的谆奥,如果狀態(tài)發(fā)生變化(比如錄制過(guò)程中,切入設(shè)置拂玻,關(guān)閉權(quán)限酸些。)會(huì)回調(diào)該方法
4.RPPreviewViewControllerDelegate
//回放預(yù)覽界面的代理方法
- (void)previewControllerDidFinish:(RPPreviewViewController *)previewController {
//用戶(hù)操作完成后宰译,返回之前的界面
[previewController dismissViewControllerAnimated:YES completion:nil];
}
//選擇了某些功能的回調(diào)(如分享和保存)
- (void)previewController:(RPPreviewViewController *)previewController didFinishWithActivityTypes:(NSSet <NSString *> *)activityTypes {
__weak ViewController *weakSelf = self;
if ([activityTypes containsObject:@"com.apple.UIKit.activity.SaveToCameraRoll"]) {
dispatch_async(dispatch_get_main_queue(), ^{
// [weakSelf showAlert:@"保存成功" andMessage:@"已經(jīng)保存到系統(tǒng)相冊(cè)"];
[MBProgressHUD showSuccess:@"已經(jīng)保存到系統(tǒng)相冊(cè)"];
});
}
if ([activityTypes containsObject:@"com.apple.UIKit.activity.CopyToPasteboard"]) {
dispatch_async(dispatch_get_main_queue(), ^{
// [weakSelf showAlert:@"復(fù)制成功" andMessage:@"已經(jīng)復(fù)制到粘貼板"];
[MBProgressHUD showSuccess:@"已經(jīng)復(fù)制到粘貼板"];
});
}
}
三.其他
1.(1)檢測(cè)版本
- (BOOL)isSystemVersionOk {
if ([[UIDevice currentDevice].systemVersion floatValue] < 9.0) {
return NO;
} else {
return YES;
}
}
(2)模擬器
#if TARGET_IPHONE_SIMULATOR
#define SIMULATOR 1
#elif TARGET_OS_IPHONE
#define SIMULATOR 0
#endif
2.處理流的操作
(1)將視頻幀中的CVPixelBufferRef轉(zhuǎn)img有顏色
-(UIImage*)pixelBuffer2Image:(CVPixelBufferRef) pixelBuffer{
CIImage *coreImage = [CIImage imageWithCVPixelBuffer:pixelBuffer];
CIContext *context = [CIContext contextWithOptions:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:kCIContextUseSoftwareRenderer]];//CPU渲染
CGImageRef cgimg = [context createCGImage:coreImage fromRect:[coreImage extent]];
UIImage* image = [UIImage imageWithCGImage:cgimg];
CFRelease(cgimg);
return image;
}
3.將視頻幀/轉(zhuǎn)換圖片(灰色的我試的是420f格式的)
- (UIImage *) imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer {
// 為媒體數(shù)據(jù)設(shè)置一個(gè)CMSampleBuffer的Core Video圖像緩存對(duì)象
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
if (imageBuffer == nil) {
return nil;
}
// 鎖定pixel buffer的基地址
CVPixelBufferLockBaseAddress(imageBuffer, 0);
void *baseAddress = CVPixelBufferGetBaseAddressOfPlane(imageBuffer,0);
// CVPixelBufferGetBaseAddress(imageBuffer);
size_t bytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer,0);
// CVPixelBufferGetBytesPerRow(imageBuffer);
// 得到pixel buffer的寬和高
size_t width = CVPixelBufferGetWidth(imageBuffer);
size_t height = CVPixelBufferGetHeight(imageBuffer);
if (width == 0 || height == 0) {return nil;}
// 創(chuàng)建一個(gè)依賴(lài)于設(shè)備的RGB顏色空間
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
// 用抽樣緩存的數(shù)據(jù)創(chuàng)建一個(gè)位圖格式的圖形上下文(graphics context)對(duì)象
CGContextRef context = CGBitmapContextCreate(baseAddress, width, height, 8,
bytesPerRow, colorSpace,kCGImageAlphaNone);
// CGAffineTransform transform = CGAffineTransformIdentity;
// CGContextConcatCTM(context, transform);
// 根據(jù)這個(gè)位圖context中的像素?cái)?shù)據(jù)創(chuàng)建一個(gè)Quartz image對(duì)象
CGImageRef quartzImage = CGBitmapContextCreateImage(context);
// 裁剪 圖片
// struct CGImage *cgImage = CGImageCreateWithImageInRect(quartzImage, CGRectMake(0, 0, height, height));
// 解鎖pixel buffer
CVPixelBufferUnlockBaseAddress(imageBuffer,0);
// 釋放context和顏色空間
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
// 用Quartz image創(chuàng)建一個(gè)UIImage對(duì)象image
UIImage *image = [UIImage imageWithCGImage:quartzImage];
// 釋放Quartz image對(duì)象
// CGImageRelease(cgImage);
CGImageRelease(quartzImage);
return (image);
}
4.流數(shù)據(jù)轉(zhuǎn)成MP4
- (void)startScreenRecording {
self.screenRecorder = [RPScreenRecorder sharedRecorder];
if (self.screenRecorder.isRecording) {
return;
}
NSError *error = nil;
NSArray *pathDocuments = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *outputURL = pathDocuments[0];
NSString *videoOutPath = [[outputURL stringByAppendingPathComponent:[NSString stringWithFormat:@"%u", arc4random() % 1000]] stringByAppendingPathExtension:@"mp4"];
self.assetWriter = [AVAssetWriter assetWriterWithURL:[NSURL fileURLWithPath:videoOutPath] fileType:AVFileTypeMPEG4 error:&error];
NSDictionary *compressionProperties =
@{
AVVideoAverageBitRateKey : [NSNumber numberWithDouble:2000 * 1000],
};
NSNumber* width= [NSNumber numberWithFloat:self.view.frame.size.width];
NSNumber* height = [NSNumber numberWithFloat:self.view.frame.size.height];
NSDictionary *videoSettings =
@{
AVVideoCompressionPropertiesKey : compressionProperties,
AVVideoCodecKey : AVVideoCodecH264,
AVVideoWidthKey : width,
AVVideoHeightKey : height};
self.assetWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoSettings];
self.assetWriterInput.expectsMediaDataInRealTime = YES;
if ([self.assetWriter canAddInput:self.assetWriterInput]) {
[self.assetWriter addInput:self.assetWriterInput];
// videoQueue = dispatch_queue_create("AVAssetWriter.videoprocessVideoQueue", DISPATCH_QUEUE_SERIAL);
}
[self.screenRecorder startCaptureWithHandler:^(CMSampleBufferRef _Nonnull sampleBuffer, RPSampleBufferType bufferType, NSError * _Nullable error) {
if (CMSampleBufferDataIsReady(sampleBuffer)) {
if (self.assetWriter.status == AVAssetWriterStatusUnknown && bufferType == RPSampleBufferTypeVideo) {
[self.assetWriter startWriting];
//丟掉無(wú)用幀
CMTime pts = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
int64_t videopts = CMTimeGetSeconds(pts) * 1000;
if(videopts < 0)
return ;
// if(lastVideoPts != videopts)
// lastVideoPts = videopts;
// else
// return ;
[self.assetWriter startSessionAtSourceTime:pts];
// startTime = CFAbsoluteTimeGetCurrent();
}
if (self.assetWriter.status == AVAssetWriterStatusFailed) {
NSLog(@"An error occured.");
[[RPScreenRecorder sharedRecorder] stopCaptureWithHandler:^(NSError * _Nullable error) {}];
return;
}
if (bufferType == RPSampleBufferTypeVideo) {
if (self.assetWriterInput.isReadyForMoreMediaData) {
// CFRetain(sampleBuffer);
// dispatch_async(videoQueue, ^{
//將sampleBuffer添加進(jìn)視頻輸入源
[self.assetWriterInput appendSampleBuffer:sampleBuffer];
// });
// CFRelease(sampleBuffer);
}else{
NSLog(@"Not ready for video");
}
}
}
} completionHandler:^(NSError * _Nullable error) {
if (!error) {
NSLog(@"Recording started successfully.");
}else{
NSLog(@"Recording started error %@",error);
}
}];
}
初學(xué)者,還有好多地方不懂魄懂,只能把我理解的寫(xiě)下來(lái),請(qǐng)多指教