diff --git a/Example_TuTu/PLShortVideoKitDemo.xcodeproj/project.pbxproj b/Example_TuTu/PLShortVideoKitDemo.xcodeproj/project.pbxproj index d9d602f3..14de8627 100644 --- a/Example_TuTu/PLShortVideoKitDemo.xcodeproj/project.pbxproj +++ b/Example_TuTu/PLShortVideoKitDemo.xcodeproj/project.pbxproj @@ -228,6 +228,7 @@ 9343A8C01F66763A007213BD /* Time_Machine_No_Reverser.png in Resources */ = {isa = PBXBuildFile; fileRef = 9343A8BE1F66763A007213BD /* Time_Machine_No_Reverser.png */; }; 9343A8C11F66763A007213BD /* Time_Machine_Reverser.png in Resources */ = {isa = PBXBuildFile; fileRef = 9343A8BF1F66763A007213BD /* Time_Machine_Reverser.png */; }; BC490FDD21B161B700959C8A /* ImageVideoMixViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BC490FDC21B161B700959C8A /* ImageVideoMixViewController.m */; }; + BC8DD6C6224E340400E05B26 /* PLScreenRecorderManager.m in Sources */ = {isa = PBXBuildFile; fileRef = BC8DD6C4224E340400E05B26 /* PLScreenRecorderManager.m */; }; BCA3CEC221B16C8F009F25B5 /* mulitRate.png in Resources */ = {isa = PBXBuildFile; fileRef = BCA3CEC021B16C8F009F25B5 /* mulitRate.png */; }; BCA3CEC421B16D06009F25B5 /* watermark.gif in Resources */ = {isa = PBXBuildFile; fileRef = BCA3CEC321B16D06009F25B5 /* watermark.gif */; }; BCA3CF5A21B22154009F25B5 /* TuSDK.bundle in Resources */ = {isa = PBXBuildFile; fileRef = BCA3CEC821B22153009F25B5 /* TuSDK.bundle */; }; @@ -662,6 +663,8 @@ 9343A8BF1F66763A007213BD /* Time_Machine_Reverser.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Time_Machine_Reverser.png; path = images/Time_Machine_Reverser.png; sourceTree = ""; }; BC490FDA21B161B700959C8A /* ImageVideoMixViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageVideoMixViewController.h; sourceTree = ""; }; BC490FDC21B161B700959C8A /* ImageVideoMixViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ImageVideoMixViewController.m; sourceTree = ""; }; + BC8DD6C4224E340400E05B26 /* PLScreenRecorderManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PLScreenRecorderManager.m; sourceTree = ""; }; + BC8DD6C5224E340400E05B26 /* PLScreenRecorderManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PLScreenRecorderManager.h; sourceTree = ""; }; BCA3CEC021B16C8F009F25B5 /* mulitRate.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = mulitRate.png; path = images/mulitRate.png; sourceTree = ""; }; BCA3CEC321B16D06009F25B5 /* watermark.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = watermark.gif; sourceTree = ""; }; BCA3CEC821B22153009F25B5 /* TuSDK.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = TuSDK.bundle; sourceTree = ""; }; @@ -1207,6 +1210,8 @@ 0A6340301F15E8CC005ADF6C /* PLSSelectionView.m */, 0A534EA8200E27F00032A981 /* PLSViewRecorderManager.h */, 0A534EAA200E27F10032A981 /* PLSViewRecorderManager.m */, + BC8DD6C5224E340400E05B26 /* PLScreenRecorderManager.h */, + BC8DD6C4224E340400E05B26 /* PLScreenRecorderManager.m */, ); path = "UI+Tools"; sourceTree = ""; @@ -1997,6 +2002,7 @@ 764FD4302112CE8B00CF1F0C /* SPARUtil.m in Sources */, BCA3CFBB21B22154009F25B5 /* EffectsView.m in Sources */, BCA3CFAC21B22154009F25B5 /* StickerPanelView.m in Sources */, + BC8DD6C6224E340400E05B26 /* PLScreenRecorderManager.m in Sources */, 0A928BE31EE75703007D2250 /* PLSAudioVolumeView.m in Sources */, 7697482C202835D2001AC847 /* TransitionViewController.m in Sources */, BCA3CFB921B22154009F25B5 /* OnlineStickerGroup.m in Sources */, diff --git a/Example_TuTu/PLShortVideoKitDemo/EditViewController.m b/Example_TuTu/PLShortVideoKitDemo/EditViewController.m index 8d54bb3b..b834f778 100644 --- a/Example_TuTu/PLShortVideoKitDemo/EditViewController.m +++ b/Example_TuTu/PLShortVideoKitDemo/EditViewController.m @@ -1219,6 +1219,9 @@ - (void)addMVLayerWithColor:(NSURL *)colorURL alpha:(NSURL *)alphaURL { } [self.shortVideoEditor addMVLayerWithColor:self.colorURL alpha:self.alphaURL timeRange:kCMTimeRangeZero loopEnable:YES]; + if (![self.shortVideoEditor isEditing]) { + [self.shortVideoEditor startEditing]; + } } - (void)addFilter:(NSString *)colorImagePath { @@ -2343,7 +2346,9 @@ - (void)nextButtonClick { } // 添加背景音乐信息 - [self.audioSettingsArray insertObject:self.backgroundAudioSettings atIndex:0]; + if (self.backgroundAudioSettings[PLSURLKey] && ![self.audioSettingsArray containsObject:self.backgroundAudioSettings]) { + [self.audioSettingsArray insertObject:self.backgroundAudioSettings atIndex:0]; + } AVAsset *asset = self.movieSettings[PLSAssetKey]; PLSAVAssetExportSession *exportSession = [[PLSAVAssetExportSession alloc] initWithAsset:asset]; @@ -2365,8 +2370,12 @@ - (void)nextButtonClick { // 旋转视频 exportSession.videoLayerOrientation = self.videoLayerOrientation; - [exportSession addFilter:self.colorImagePath]; - [exportSession addMVLayerWithColor:self.colorURL alpha:self.alphaURL timeRange:kCMTimeRangeZero loopEnable:YES]; + if (self.colorImagePath) { + [exportSession addFilter:self.colorImagePath]; + } + if (self.colorURL && self.alphaURL) { + [exportSession addMVLayerWithColor:self.colorURL alpha:self.alphaURL timeRange:kCMTimeRangeZero loopEnable:YES]; + } __weak typeof(self) weakSelf = self; [exportSession setCompletionBlock:^(NSURL *url) { @@ -2397,7 +2406,9 @@ - (void)nextButtonClick { [exportSession setProcessingBlock:^(float progress) { // 更新进度 UI NSLog(@"Asset Export Progress: %f", progress); - weakSelf.progressLabel.text = [NSString stringWithFormat:@"%d%%", (int)(progress * 100)]; + dispatch_async(dispatch_get_main_queue(), ^{ + weakSelf.progressLabel.text = [NSString stringWithFormat:@"%d%%", (int)(progress * 100)]; + }); }]; [exportSession exportAsynchronously]; diff --git a/Example_TuTu/PLShortVideoKitDemo/Info.plist b/Example_TuTu/PLShortVideoKitDemo/Info.plist index 00e5d40a..c0554a9e 100644 --- a/Example_TuTu/PLShortVideoKitDemo/Info.plist +++ b/Example_TuTu/PLShortVideoKitDemo/Info.plist @@ -15,9 +15,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.16.1 + 2.0.0 CFBundleVersion - 1.16.1.git-2019-01-28-1f597d9 + 2.0.0.git-2019-03-29-1cdc0b4 Fabric APIKey diff --git a/Example_TuTu/PLShortVideoKitDemo/Librarys/libMuseProcessor.a b/Example_TuTu/PLShortVideoKitDemo/Librarys/libMuseProcessor.a index bc296285..ed5e2127 100644 Binary files a/Example_TuTu/PLShortVideoKitDemo/Librarys/libMuseProcessor.a and b/Example_TuTu/PLShortVideoKitDemo/Librarys/libMuseProcessor.a differ diff --git a/Example_TuTu/PLShortVideoKitDemo/RecordViewController.m b/Example_TuTu/PLShortVideoKitDemo/RecordViewController.m index 442d9755..e2b930b3 100644 --- a/Example_TuTu/PLShortVideoKitDemo/RecordViewController.m +++ b/Example_TuTu/PLShortVideoKitDemo/RecordViewController.m @@ -17,6 +17,7 @@ #import "PLSFilterGroup.h" #import "PLSViewRecorderManager.h" #import "PLSRateButtonView.h" +#import "PLScreenRecorderManager.h" // AR #import "EasyarARViewController.h" @@ -57,6 +58,7 @@ @interface RecordViewController () UICollectionViewDelegateFlowLayout, PLSViewRecorderManagerDelegate, PLSRateButtonViewDelegate, +PLScreenRecorderManagerDelegate, // TuSDK mark - delegate TuSDKFilterProcessorDelegate, TuSDKFilterProcessorMediaEffectDelegate, @@ -85,6 +87,7 @@ @interface RecordViewController () @property (strong, nonatomic) PLSAudioConfiguration *audioConfiguration; @property (strong, nonatomic) PLShortVideoRecorder *shortVideoRecorder; @property (strong, nonatomic) PLSViewRecorderManager *viewRecorderManager; +@property (strong, nonatomic) PLScreenRecorderManager *screenRecorderManager; @property (strong, nonatomic) PLSProgressBar *progressBar; @property (strong, nonatomic) UIButton *recordButton; @property (strong, nonatomic) UIButton *viewRecordButton; @@ -129,6 +132,8 @@ @interface RecordViewController () @property (strong, nonatomic) UIButton *monitorButton; // 实时截图按钮 @property (strong, nonatomic) UIButton *snapshotButton; +// 帧率切换按钮 +@property (strong, nonatomic) UIButton *frameRateButton; // 录制前是否开启自动检测设备方向调整视频拍摄的角度(竖屏、横屏) @property (assign, nonatomic) BOOL isUseAutoCheckDeviceOrientationBeforeRecording; @@ -183,7 +188,8 @@ - (instancetype)init { _audioSampleArray = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL); _audioTimeStampArray = [[NSMutableArray alloc] init]; - NSLog(@"TuSDK version: %@", lsqVideoVersion); + NSLog(@"TuSDK version: %@", lsqSDKVersion); + NSLog(@"TuSDK video version: %@", lsqVideoVersion); } return self; } @@ -242,10 +248,11 @@ - (void)setupShortVideoRecorder { self.videoConfiguration = [PLSVideoConfiguration defaultConfiguration]; self.videoConfiguration.position = AVCaptureDevicePositionFront; - self.videoConfiguration.videoFrameRate = 25; - self.videoConfiguration.averageVideoBitRate = 1024*1000; - self.videoConfiguration.videoSize = CGSizeMake(544, 960); + self.videoConfiguration.videoFrameRate = 30; + self.videoConfiguration.averageVideoBitRate = 1000*2500; + self.videoConfiguration.videoSize = CGSizeMake(720, 1280); self.videoConfiguration.videoOrientation = AVCaptureVideoOrientationPortrait; + self.videoConfiguration.sessionPreset = AVCaptureSessionPreset1280x720; self.audioConfiguration = [PLSAudioConfiguration defaultConfiguration]; @@ -486,6 +493,17 @@ - (void)setupRightButtonView { [self.musicButton addTarget:self action:@selector(musicButtonOnClick:) forControlEvents:UIControlEventTouchUpInside]; [self.rightScrollView addSubview:self.musicButton]; + index ++; + // 30FPS/60FPS + self.frameRateButton = [[UIButton alloc] initWithFrame:CGRectMake(0, index * 60 + 10, 46, 46)]; + self.frameRateButton.layer.cornerRadius = 23; + self.frameRateButton.backgroundColor = backgroundColor; + [self.frameRateButton setTitle:@"30帧" forState:(UIControlStateNormal)]; + self.frameRateButton.titleLabel.font = [UIFont systemFontOfSize:14]; + [self.frameRateButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; + [self.frameRateButton addTarget:self action:@selector(frameRateButtonOnClick:) forControlEvents:UIControlEventTouchUpInside]; + [self.rightScrollView addSubview:self.frameRateButton]; + index ++; // AR UIButton *ARButton = [[UIButton alloc] initWithFrame:CGRectMake(0, index * 60 + 10, 46, 46)]; @@ -497,17 +515,6 @@ - (void)setupRightButtonView { [self.rightScrollView addSubview:ARButton]; index ++; - // 外部滤镜 -// UIButton *externalFilterButton = [[UIButton alloc] initWithFrame:CGRectMake(0, index * 60 + 10, 46, 46)]; -// externalFilterButton.layer.cornerRadius = 23; -// externalFilterButton.backgroundColor = backgroundColor; -// [externalFilterButton setTitle:@"美化" forState:UIControlStateNormal]; -// [externalFilterButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; -// externalFilterButton.titleLabel.font = [UIFont systemFontOfSize:13]; -// [externalFilterButton addTarget:self action:@selector(externalFilterButtonOnClick:) forControlEvents:UIControlEventTouchUpInside]; -// [self.rightScrollView addSubview:externalFilterButton]; -// -// index ++; // 滤镜按钮 _filterBtn = [[UIButton alloc]initWithFrame:CGRectMake(0, index * 60 + 10, 46, 46)]; _filterBtn.layer.cornerRadius = 23; @@ -693,6 +700,7 @@ - (void)getFirstMovieFromPhotoAlbum { - (void)backButtonEvent:(id)sender { if (self.viewRecordButton.isSelected) { [self.viewRecorderManager cancelRecording]; + [self.screenRecorderManager cancelRecording]; } if ([self.shortVideoRecorder getFilesCount] > 0) { self.alertView = [[UIAlertView alloc] initWithTitle:@"提醒" message:[NSString stringWithFormat:@"放弃这个视频(共%ld个视频段)?", (long)[self.shortVideoRecorder getFilesCount]] delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil]; @@ -708,7 +716,7 @@ - (void)squareRecordButtonEvent:(id)sender { UIButton *button = (UIButton *)sender; button.selected = !button.selected; if (button.selected) { - self.videoConfiguration.videoSize = CGSizeMake(480, 480); + self.videoConfiguration.videoSize = CGSizeMake(720, 720); [self.shortVideoRecorder reloadvideoConfiguration:self.videoConfiguration]; self.shortVideoRecorder.maxDuration = 10.0f; @@ -720,7 +728,7 @@ - (void)squareRecordButtonEvent:(id)sender { }); } else { - self.videoConfiguration.videoSize = CGSizeMake(544, 960); + self.videoConfiguration.videoSize = CGSizeMake(720, 1280); [self.shortVideoRecorder reloadvideoConfiguration:self.videoConfiguration]; self.shortVideoRecorder.maxDuration = 10.0f; @@ -734,26 +742,39 @@ - (void)squareRecordButtonEvent:(id)sender { //录制 self.view - (void)viewRecorderButtonClick:(id)sender { - if (!self.viewRecorderManager) { - self.viewRecorderManager = [[PLSViewRecorderManager alloc] initWithRecordedView:self.view]; - self.viewRecorderManager.delegate = self; - } - - if (self.viewRecordButton.isSelected) { - self.viewRecordButton.selected = NO; - [self.viewRecorderManager stopRecording]; - - [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillResignActiveNotification object:nil]; - } - else { - self.viewRecordButton.selected = YES; - [self clearAudioPitchBuffer]; - [self.viewRecorderManager startRecording]; + if (@available(iOS 11.0, *)) { + if (!self.screenRecorderManager) { + self.screenRecorderManager = [[PLScreenRecorderManager alloc] init]; + self.screenRecorderManager.delegate = self; + } + if (self.viewRecordButton.isSelected) { + self.viewRecordButton.selected = NO; + [self.screenRecorderManager stopRecording]; + } else { + self.viewRecordButton.selected = YES; + [self.screenRecorderManager startRecording]; + } + } else { + if (!self.viewRecorderManager) { + self.viewRecorderManager = [[PLSViewRecorderManager alloc] initWithRecordedView:self.shortVideoRecorder.previewView]; + self.viewRecorderManager.delegate = self; + } - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(applicationWillResignActive:) - name:UIApplicationWillResignActiveNotification - object:nil]; + if (self.viewRecordButton.isSelected) { + self.viewRecordButton.selected = NO; + [self.viewRecorderManager stopRecording]; + + [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillResignActiveNotification object:nil]; + } + else { + self.viewRecordButton.selected = YES; + [self.viewRecorderManager startRecording]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(applicationWillResignActive:) + name:UIApplicationWillResignActiveNotification + object:nil]; + } } } @@ -776,8 +797,22 @@ - (void)beautyFaceButtonEvent:(id)sender { } // 切换前后置摄像头 -- (void)toggleCameraButtonEvent:(id)sender { - [self.shortVideoRecorder toggleCamera]; +- (void)toggleCameraButtonEvent:(UIButton *)sender { + // 采集帧率不大于 30 帧的时候,使用 [self.shortVideoRecorder toggleCamera] 和 [self.shortVideoRecorder toggleCamera:block] 都可以。当采集大于 30 帧的时候,为确保切换成功,需要先停止采集,再切换相机,切换完成再启动采集。如果不先停止采集,部分机型上采集 60 帧的时候,切换摄像头可能会耗时几秒钟 + if (self.videoConfiguration.videoFrameRate > 30) { + sender.enabled = NO; + __weak typeof(self) weakself = self; + [self.shortVideoRecorder stopCaptureSession]; + [self.shortVideoRecorder toggleCamera:^(BOOL isFinish) { + [weakself checkActiveFormat];// 默认的 active 可能最大只支持采集 30 帧,这里手动设置一下 + [weakself.shortVideoRecorder startCaptureSession]; + dispatch_async(dispatch_get_main_queue(), ^{ + sender.enabled = YES; + }); + }]; + } else { + [self.shortVideoRecorder toggleCamera]; + } } // 七牛滤镜 @@ -821,6 +856,23 @@ - (void)musicButtonOnClick:(id)sender { } } +- (void)frameRateButtonOnClick:(UIButton *)button { + if (60 == self.videoConfiguration.videoFrameRate) { + self.videoConfiguration.videoFrameRate = 30; + self.videoConfiguration.averageVideoBitRate = 1000 * 2500; + self.videoConfiguration.sessionPreset = AVCaptureSessionPreset1280x720; + [button setTitle:@"30帧" forState:(UIControlStateNormal)]; + [self.shortVideoRecorder reloadvideoConfiguration:self.videoConfiguration]; + } else { + self.videoConfiguration.videoFrameRate = 60; + self.videoConfiguration.averageVideoBitRate = 1000 * 3500; + self.videoConfiguration.sessionPreset = AVCaptureSessionPresetInputPriority; + [button setTitle:@"60帧" forState:(UIControlStateNormal)]; + [self.shortVideoRecorder reloadvideoConfiguration:self.videoConfiguration]; + [self checkActiveFormat]; + } +} + // 拍照 -(void)snapshotButtonOnClick:(UIButton *)sender { sender.enabled = NO; @@ -917,6 +969,7 @@ - (void)endButtonEvent:(id)sender { AVAsset *asset = self.shortVideoRecorder.assetRepresentingAllFiles; [self playEvent:asset]; [self.viewRecorderManager cancelRecording]; + [self.screenRecorderManager cancelRecording]; self.viewRecordButton.selected = NO; } @@ -1007,6 +1060,31 @@ - (void)viewRecorderManager:(PLSViewRecorderManager *)manager didFinishRecording [self presentViewController:videoEditViewController animated:YES completion:nil]; } +#pragma mark - PLScreenRecorderManagerDelegate +- (void)screenRecorderManager:(PLScreenRecorderManager *)manager didFinishRecordingToAsset:(AVAsset *)asset totalDuration:(CGFloat)totalDuration { + self.viewRecordButton.selected = NO; + // 设置音视频、水印等编辑信息 + NSMutableDictionary *outputSettings = [[NSMutableDictionary alloc] init]; + // 待编辑的原始视频素材 + NSMutableDictionary *plsMovieSettings = [[NSMutableDictionary alloc] init]; + plsMovieSettings[PLSAssetKey] = asset; + plsMovieSettings[PLSStartTimeKey] = [NSNumber numberWithFloat:0.f]; + plsMovieSettings[PLSDurationKey] = [NSNumber numberWithFloat:totalDuration]; + plsMovieSettings[PLSVolumeKey] = [NSNumber numberWithFloat:1.0f]; + outputSettings[PLSMovieSettingsKey] = plsMovieSettings; + + EditViewController *videoEditViewController = [[EditViewController alloc] init]; + videoEditViewController.settings = outputSettings; + [self presentViewController:videoEditViewController animated:YES completion:nil]; +} + +- (void)screenRecorderManager:(PLScreenRecorderManager *)manager errorOccur:(NSError *)error { + NSString *message = [NSString stringWithFormat:@"%@", error]; + UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"错误" message:message delegate:nil cancelButtonTitle:@"确定" otherButtonTitles: nil]; + [alert show]; + self.viewRecordButton.selected = NO; +} + #pragma mark -- PLShortVideoRecorderDelegate 摄像头/麦克风鉴权的回调 - (void)shortVideoRecorder:(PLShortVideoRecorder *__nonnull)recorder didGetCameraAuthorizationStatus:(PLSAuthorizationStatus)status { if (status == PLSAuthorizationStatusAuthorized) { @@ -1105,6 +1183,7 @@ - (void)shortVideoRecorder:(PLShortVideoRecorder *)recorder didRecordingToOutput self.importMovieView.hidden = YES; self.musicButton.hidden = YES; self.filePathButton.hidden = YES; + self.frameRateButton.hidden = YES; self.durationLabel.text = [NSString stringWithFormat:@"%.2fs", totalDuration]; } @@ -1122,6 +1201,7 @@ - (void)shortVideoRecorder:(PLShortVideoRecorder *)recorder didDeleteFileAtURL:( self.importMovieView.hidden = NO; self.musicButton.hidden = NO; self.filePathButton.hidden = NO; + self.frameRateButton.hidden = NO; } AVAsset *asset = [AVAsset assetWithURL:_URL]; @@ -1334,58 +1414,22 @@ - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPa - (void)setupGestureRecognizer { UISwipeGestureRecognizer *recognizer; // 添加右滑手势 - recognizer = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(handleSwipeFrom:)]; + recognizer = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(handleFilterSwipeFrom:)]; [recognizer setDirection:(UISwipeGestureRecognizerDirectionRight)]; [self.view addGestureRecognizer:recognizer]; - recognizer.delegate = self; // 添加左滑手势 - recognizer = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(handleSwipeFrom:)]; + recognizer = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(handleFilterSwipeFrom:)]; [recognizer setDirection:(UISwipeGestureRecognizerDirectionLeft)]; [self.view addGestureRecognizer:recognizer]; - recognizer.delegate = self; - // 添加上滑手势 - recognizer = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(handleSwipeFrom:)]; - [recognizer setDirection:(UISwipeGestureRecognizerDirectionUp)]; - [self.view addGestureRecognizer:recognizer]; - recognizer.delegate = self; // 添加下滑手势 - recognizer = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(handleSwipeFrom:)]; + recognizer = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(handleDownSwipeFrom:)]; [recognizer setDirection:(UISwipeGestureRecognizerDirectionDown)]; [self.view addGestureRecognizer:recognizer]; - recognizer.delegate = self; } --(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { - BOOL result = YES; - if (_filterView) { - result = result && _filterView.hidden; - } - if (_stickerView) { - result = result && _stickerView.hidden; - } - if (_facePanelView) { - result = result && _facePanelView.hidden; - } - if (_cartoonView) { - result = result && _cartoonView.hidden; - } - return result; -} // 添加手势的响应事件 -- (void)handleSwipeFrom:(UISwipeGestureRecognizer *)recognizer{ - if(recognizer.direction == UISwipeGestureRecognizerDirectionDown) { - NSLog(@"swipe down"); - self.filterIndex++; - self.filterIndex %= self.filterGroup.filtersInfo.count; - } - if(recognizer.direction == UISwipeGestureRecognizerDirectionUp) { - NSLog(@"swipe up"); - self.filterIndex--; - if (self.filterIndex < 0) { - self.filterIndex = self.filterGroup.filtersInfo.count - 1; - } - } +- (void)handleFilterSwipeFrom:(UISwipeGestureRecognizer *)recognizer{ if(recognizer.direction == UISwipeGestureRecognizerDirectionLeft) { NSLog(@"swipe left"); self.filterIndex--; @@ -1403,6 +1447,45 @@ - (void)handleSwipeFrom:(UISwipeGestureRecognizer *)recognizer{ self.filterGroup.filterIndex = self.filterIndex; } +- (void)handleDownSwipeFrom:(UISwipeGestureRecognizer *)recognizer{ + if(recognizer.direction == UISwipeGestureRecognizerDirectionDown) { + NSLog(@"swipe down"); + if ([self isViewShow:_filterView]) { + [self hideView:_filterView]; + } + if ([self isViewShow:_cartoonView]) { + [self hideView:_cartoonView]; + } + if ([self isViewShow:_stickerView]) { + [self hideView:_stickerView]; + } + if ([self isViewShow:_facePanelView]) { + [self hideView:_facePanelView]; + } + } +} + +- (void)showView:(UIView *)view { + CGRect rect = view.frame; + rect.origin.y = self.view.bounds.size.height - view.frame.size.height; + [UIView animateWithDuration:.3 animations:^{ + view.frame = rect; + }]; +} + +- (void)hideView:(UIView *)view { + CGRect rect = view.frame; + rect.origin.y = self.view.bounds.size.height; + [UIView animateWithDuration:.3 animations:^{ + view.frame = rect; + }]; +} + +- (BOOL)isViewShow:(UIView *)view { + if (!view) return NO; + return fabs(self.view.bounds.size.height - view.frame.origin.y) > FLT_EPSILON; +} + #pragma mark - addObserverEvent - (void)addObserverEvent { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil]; @@ -1423,6 +1506,41 @@ - (void)applicationDidBecomeActive:(id)sender { NSLog(@"%s, %d, applicationDidBecomeActive:", __func__, __LINE__); } +- (void)checkActiveFormat { + + CGSize needCaptureSize = self.videoConfiguration.videoSize; + + if (AVCaptureVideoOrientationPortrait == self.videoConfiguration.videoOrientation || + AVCaptureVideoOrientationPortraitUpsideDown == self.videoConfiguration.videoOrientation) { + needCaptureSize = CGSizeMake(self.videoConfiguration.videoSize.height, self.videoConfiguration.videoSize.width); + } + + AVCaptureDeviceFormat *activeFormat = self.shortVideoRecorder.videoActiveFormat; + AVFrameRateRange *frameRateRange = [activeFormat.videoSupportedFrameRateRanges firstObject]; + + CMVideoDimensions captureSize = CMVideoFormatDescriptionGetDimensions(activeFormat.formatDescription); + if (frameRateRange.maxFrameRate < self.videoConfiguration.videoFrameRate || + frameRateRange.minFrameRate > self.videoConfiguration.videoFrameRate || + needCaptureSize.width > captureSize.width || + needCaptureSize.height > captureSize.height) { + + NSArray *videoFormats = self.shortVideoRecorder.videoFormats; + for (AVCaptureDeviceFormat *format in videoFormats) { + frameRateRange = [format.videoSupportedFrameRateRanges firstObject]; + captureSize = CMVideoFormatDescriptionGetDimensions(format.formatDescription); + + if (frameRateRange.maxFrameRate >= self.videoConfiguration.videoFrameRate && + frameRateRange.minFrameRate <= self.videoConfiguration.videoFrameRate && + captureSize.width >= needCaptureSize.width && + captureSize.height >= needCaptureSize.height) { + NSLog(@"size = {%d x %d}, fps = %f ~ %f", captureSize.width, captureSize.height, frameRateRange.minFrameRate, frameRateRange.maxFrameRate); + self.shortVideoRecorder.videoActiveFormat = format; + break; + } + } + } +} + #pragma mark - EasyarSDK AR 入口 - (void)ARButtonOnClick:(id)sender { EasyarARViewController *easyerARViewController = [[EasyarARViewController alloc]init]; @@ -1440,32 +1558,6 @@ - (void)checkBundleId { } } -- (void)externalFilterButtonOnClick:(UIButton *)button { - [self checkBundleId]; - - if (!_filterView) { -// [self initFilterView]; - } - - _filterView.hidden = !_filterView.hidden; - if (!_filterView.hidden) { - _stickerView.hidden = YES; - } -} - -- (void)externalStickerButtonOnClick:(UIButton *)button { - [self checkBundleId]; - - if (!_stickerView) { -// [self initStickerView]; - } - - _stickerView.hidden = !_stickerView.hidden; - if (!_stickerView.hidden) { - _filterView.hidden = YES; - } -} - - (void)initTUSDK { // TuSDK mark [self initFilterProcessor]; @@ -1567,12 +1659,30 @@ - (void)clickStickerBtn; _stickerView.delegate = (id)self; CGSize size = self.view.bounds.size; const CGFloat stickerPanelHeight = 200; - _stickerView.frame = CGRectMake(0, size.height - stickerPanelHeight, size.width, stickerPanelHeight); + _stickerView.frame = CGRectMake(0, size.height, size.width, stickerPanelHeight); [self.view addSubview:_stickerView]; - }else{ - _stickerView.hidden = !_stickerView.hidden; + + UIVisualEffect *effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]; + UIVisualEffectView *effectView = [[UIVisualEffectView alloc] initWithEffect:effect]; + effectView.frame = _stickerView.bounds; + [_stickerView insertSubview:effectView atIndex:0]; + } + + if ([self isViewShow:_cartoonView]) { + [self hideView:_cartoonView]; + } + if ([self isViewShow:_facePanelView]) { + [self hideView:_facePanelView]; + } + if ([self isViewShow:_filterView]) { + [self hideView:_filterView]; + } + + if ([self isViewShow:_stickerView]) { + [self hideView:_stickerView]; + } else { + [self showView:_stickerView]; } - _cartoonView.hidden =_facePanelView.hidden = _filterView.hidden = YES; } - (void)clickFilterBtn; @@ -1583,16 +1693,29 @@ - (void)clickFilterBtn; CGFloat filterPanelHeight = 276; // 滤镜视图 - _filterView = [[FilterPanelView alloc] initWithFrame:CGRectMake(0, size.height - filterPanelHeight, size.width, filterPanelHeight)]; + _filterView = [[FilterPanelView alloc] initWithFrame:CGRectMake(0, size.height, size.width, filterPanelHeight)]; _filterView.delegate = (id)self; _filterView.dataSource = (id)self; _filterView.codes = @[kCameraFilterCodes]; [self.view addSubview:_filterView]; - }else{ - _filterView.hidden = !_filterView.hidden; } - _cartoonView.hidden = _facePanelView.hidden = _stickerView.hidden = YES; + + if ([self isViewShow:_cartoonView]) { + [self hideView:_cartoonView]; + } + if ([self isViewShow:_facePanelView]) { + [self hideView:_facePanelView]; + } + if ([self isViewShow:_stickerView]) { + [self hideView:_stickerView]; + } + + if ([self isViewShow:_filterView]) { + [self hideView:_filterView]; + } else { + [self showView:_filterView]; + } } - (void)clickFaceBtn; @@ -1604,12 +1727,25 @@ - (void)clickFaceBtn; _facePanelView.dataSource = (id)self; CGSize size = self.view.bounds.size; const CGFloat filterPanelHeight = 276; - _facePanelView.frame = CGRectMake(0, size.height - filterPanelHeight, size.width, filterPanelHeight); + _facePanelView.frame = CGRectMake(0, size.height, size.width, filterPanelHeight); [self.view addSubview:_facePanelView]; - }else{ - _facePanelView.hidden = !_facePanelView.hidden; } - _cartoonView.hidden = _stickerView.hidden = _filterView.hidden = YES; + + if ([self isViewShow:_cartoonView]) { + [self hideView:_cartoonView]; + } + if ([self isViewShow:_filterView]) { + [self hideView:_filterView]; + } + if ([self isViewShow:_stickerView]) { + [self hideView:_stickerView]; + } + + if ([self isViewShow:_facePanelView]) { + [self hideView:_facePanelView]; + } else { + [self showView:_facePanelView]; + } } - (void)clickCartoonBtn; @@ -1620,15 +1756,27 @@ - (void)clickCartoonBtn; CGFloat filterPanelHeight = 276; // 滤镜视图 - _cartoonView = [[CartoonPanelView alloc] initWithFrame:CGRectMake(0, size.height - filterPanelHeight, size.width, filterPanelHeight)]; + _cartoonView = [[CartoonPanelView alloc] initWithFrame:CGRectMake(0, size.height, size.width, filterPanelHeight)]; _cartoonView.delegate = self; [self.view addSubview:_cartoonView]; - }else{ - _cartoonView.hidden = !_cartoonView.hidden; } - _filterView.hidden = _facePanelView.hidden = _stickerView.hidden = YES; + if ([self isViewShow:_facePanelView]) { + [self hideView:_facePanelView]; + } + if ([self isViewShow:_filterView]) { + [self hideView:_filterView]; + } + if ([self isViewShow:_stickerView]) { + [self hideView:_stickerView]; + } + + if ([self isViewShow:_cartoonView]) { + [self hideView:_cartoonView]; + } else { + [self showView:_cartoonView]; + } } /** diff --git a/Example_TuTu/PLShortVideoKitDemo/SegmentEffects/MulitClipViewController.m b/Example_TuTu/PLShortVideoKitDemo/SegmentEffects/MulitClipViewController.m index c1da9c87..9f9de124 100644 --- a/Example_TuTu/PLShortVideoKitDemo/SegmentEffects/MulitClipViewController.m +++ b/Example_TuTu/PLShortVideoKitDemo/SegmentEffects/MulitClipViewController.m @@ -369,7 +369,7 @@ - (void)clipView:(PLSClipMulitMediaView *)clipView finishClip:(NSArray +#import + +NS_ASSUME_NONNULL_BEGIN + +@class PLScreenRecorderManager; + +@protocol PLScreenRecorderManagerDelegate + +@optional +- (void)screenRecorderManager:(PLScreenRecorderManager *)manager didFinishRecordingToAsset:(AVAsset *)asset totalDuration:(CGFloat)totalDuration; +- (void)screenRecorderManager:(PLScreenRecorderManager *)manager errorOccur:(NSError *)error; +@end + +@interface PLScreenRecorderManager : NSObject + +@property (weak, nonatomic) id delegate; + +- (void)startRecording; + +- (void)stopRecording; + +- (void)cancelRecording; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Example_TuTu/PLShortVideoKitDemo/UI+Tools/PLScreenRecorderManager.m b/Example_TuTu/PLShortVideoKitDemo/UI+Tools/PLScreenRecorderManager.m new file mode 100644 index 00000000..5b4c5bbf --- /dev/null +++ b/Example_TuTu/PLShortVideoKitDemo/UI+Tools/PLScreenRecorderManager.m @@ -0,0 +1,152 @@ +// +// PLScreenRecorderManager.m +// PLShortVideoKitDemo +// +// Created by hxiongan on 2019/3/28. +// Copyright © 2019年 Pili Engineering, Qiniu Inc. All rights reserved. +// + +#import "PLScreenRecorderManager.h" +#import +#include +#import + +@interface PLScreenRecorderManager () + +@property (nonatomic, strong) PLShortVideoRecorder *shortVideoRecorder; +@property (nonatomic, weak) RPScreenRecorder *screenRecorder; + +@end + + +@implementation PLScreenRecorderManager + +- (instancetype)init { + self = [super init]; + if (self) { + [self setupShortRecorder]; + [self setupScreenRecorder]; + } + return self; +} + +- (void)setupShortRecorder { + PLSVideoConfiguration *videoConfiguration = [PLSVideoConfiguration defaultConfiguration]; + videoConfiguration.videoFrameRate = 60; + videoConfiguration.videoSize = CGSizeMake(720, 1280); + videoConfiguration.averageVideoBitRate = 3500 * 1000; + PLSAudioConfiguration *audioConfiguration = [PLSAudioConfiguration defaultConfiguration]; + self.shortVideoRecorder = [[PLShortVideoRecorder alloc] initWithVideoConfiguration:videoConfiguration audioConfiguration:audioConfiguration captureEnabled:NO]; + self.shortVideoRecorder.maxDuration = 120; + self.shortVideoRecorder.delegate = self; + self.shortVideoRecorder.backgroundMonitorEnable = NO; +} + +- (void)setupScreenRecorder { + self.screenRecorder = [RPScreenRecorder sharedRecorder]; + self.screenRecorder.microphoneEnabled = YES; +} + +- (void)startRecording { + __weak typeof(self) weakSelf = self; + if (@available(iOS 11.0, *)) { + + [weakSelf.screenRecorder startCaptureWithHandler:^(CMSampleBufferRef _Nonnull sampleBuffer, RPSampleBufferType bufferType, NSError * _Nullable error) { + if (RPSampleBufferTypeVideo == bufferType) { + [weakSelf.shortVideoRecorder writePixelBuffer:CMSampleBufferGetImageBuffer(sampleBuffer) timeStamp:CMSampleBufferGetPresentationTimeStamp(sampleBuffer)]; + } else if (RPSampleBufferTypeAudioMic == bufferType){ + [weakSelf.shortVideoRecorder writeSampleBuffer:sampleBuffer]; + } + } completionHandler:^(NSError * _Nullable error) { + if (error) { + dispatch_async(dispatch_get_main_queue(), ^{ + if ([weakSelf.delegate respondsToSelector:@selector(screenRecorderManager:errorOccur:)]) { + [weakSelf.delegate screenRecorderManager:weakSelf errorOccur:error]; + } + [weakSelf cancelRecording]; + }); + } else { + dispatch_sync(dispatch_get_main_queue(), ^{ + // 注意: [shortVideoRecorder startRecording] 需要再主线程中执行,不然拍的时长会不正确 + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil]; + [weakSelf.shortVideoRecorder deleteAllFiles]; + [weakSelf.shortVideoRecorder startRecording]; + }); + } + }]; + } else { + // Fallback on earlier versions + } +} + +- (void)stopRecording { + if (@available(iOS 11.0, *)) { + [self.screenRecorder stopCaptureWithHandler:^(NSError * _Nullable error) { + if (error) { + NSLog(@"screenRecorder stop error: %@", error); + } + }]; + } else { + // Fallback on earlier versions + } + [self.shortVideoRecorder stopRecording]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil]; +} + +- (void)cancelRecording { + if (@available(iOS 11.0, *)) { + [self.screenRecorder stopCaptureWithHandler:^(NSError * _Nullable error) { + if (error) { + NSLog(@"screenRecorder stop error: %@", error); + } + }]; + } else { + // Fallback on earlier versions + } + NSLog(@"cancel"); + [self.shortVideoRecorder cancelRecording]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil]; +} + +- (void)applicationDidEnterBackground:(id)sender { + [self stopRecording]; +} + +- (void)dealloc { + [self cancelRecording]; + NSLog(@"dealloc: %@", self.description); +} + +#pragma mark - Recorder Delegate + +// 开始录制一段视频时 +- (void)shortVideoRecorder:(PLShortVideoRecorder *)recorder didStartRecordingToOutputFileAtURL:(NSURL *)fileURL { + NSLog(@"start recording fileURL: %@", fileURL); +} + +// 完成一段视频的录制时 +- (void)shortVideoRecorder:(PLShortVideoRecorder *)recorder didFinishRecordingToOutputFileAtURL:(NSURL *)fileURL fileDuration:(CGFloat)fileDuration totalDuration:(CGFloat)totalDuration { + NSLog(@"finish recording fileURL: %@, fileDuration: %f, totalDuration: %f", fileURL, fileDuration, totalDuration); + + AVAsset *asset = self.shortVideoRecorder.assetRepresentingAllFiles; + if (self.delegate && [self.delegate respondsToSelector:@selector(screenRecorderManager:didFinishRecordingToAsset:totalDuration:)]) { + [self.delegate screenRecorderManager:self didFinishRecordingToAsset:asset totalDuration:totalDuration]; + } +} + +// 在达到指定的视频录制时间 maxDuration 后,如果再调用 [PLShortVideoRecorder startRecording],直接执行该回调 +- (void)shortVideoRecorder:(PLShortVideoRecorder *)recorder didFinishRecordingMaxDuration:(CGFloat)maxDuration { + NSLog(@"finish recording maxDuration: %f", maxDuration); + if (@available(iOS 11.0, *)) { + [self.screenRecorder stopCaptureWithHandler:^(NSError * _Nullable error) {}]; + } else { + // Fallback on earlier versions + } + AVAsset *asset = self.shortVideoRecorder.assetRepresentingAllFiles; + if (self.delegate && [self.delegate respondsToSelector:@selector(screenRecorderManager:didFinishRecordingToAsset:totalDuration:)]) { + [self.delegate screenRecorderManager:self didFinishRecordingToAsset:asset totalDuration:maxDuration]; + } +} + +@end +