UGCKitRecordViewController.m 73 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944
  1. // Copyright (c) 2019 Tencent. All rights reserved.
  2. #import "UGCKitRecordViewController.h"
  3. #import <MediaPlayer/MediaPlayer.h>
  4. #import <TCBeautyPanel/TCBeautyPanel.h>
  5. #import "SDKHeader.h"
  6. #import "UGCKitMem.h"
  7. #import "UGCKitMediaPickerViewController.h"
  8. #import "UGCKitEditViewController.h"
  9. #import "UGCKitVideoRecordMusicView.h"
  10. #import "UGCKitVideoRecordProcessView.h"
  11. #import "UGCKitProgressHUD.h"
  12. #import "UGCKitBGMListViewController.h"
  13. #import "UGCKitAudioEffectPanel.h"
  14. #import "UGCKitLabel.h"
  15. #import "UGCKitTheme.h"
  16. #import "UGCKitSlideButton.h"
  17. #import "UGCKitRecordControlView.h"
  18. #import "UGCKitSmallButton.h"
  19. #import "UGCKit_UIViewAdditions.h"
  20. #import "UGCKitConstants.h"
  21. #import <objc/runtime.h>
  22. #import "UGCKitReporterInternal.h"
  23. #import "SDKHeader.h"
  24. static const CGFloat BUTTON_CONTROL_SIZE = 40;
  25. static const CGFloat AudioEffectViewHeight = 150;
  26. typedef NS_ENUM(NSInteger, CaptureMode) {
  27. CaptureModeStill,
  28. CaptureModeTap,
  29. CaptureModePress
  30. };
  31. typedef NS_ENUM(NSInteger, RecordState) {
  32. RecordStateStopped,
  33. RecordStateRecording,
  34. RecordStatePaused
  35. };
  36. #if POD_PITU
  37. #import "MCCameraDynamicView.h"
  38. #import "MaterialManager.h"
  39. #import "MCTip.h"
  40. #endif
  41. @interface UGCKitRecordPreviewController : NSObject
  42. @property (nonatomic, strong, readonly) NSArray<NSNumber *> *allVideoVolumes;
  43. @property (nonatomic, strong, readonly) NSArray<NSString *> *allVideoPaths;
  44. @property (nonatomic, strong, readonly) NSString *recordVideoPath;
  45. @property (nonatomic, strong, readonly) UIView *videoRecordView;
  46. - (instancetype)initWithContainerView:(UIView *)containerView
  47. recordStyle:(UGCKitRecordStyle)recordStyle
  48. chorusVideos:(NSArray<NSString *> *)chorusVideos
  49. recordVideo:(NSString *)recordVideo;
  50. - (void)changeChorusVideo:(NSString *)videoPath;
  51. - (void)startPlayChorusVideos:(CGFloat)startTime
  52. toTime:(CGFloat)endTime;
  53. - (void)stopPlayChorusVideos;
  54. - (void)seekChorusVideosToTime:(CGFloat)time;
  55. @end
  56. @interface UGCKitNavControllerPrivate : UINavigationController
  57. @property (nonatomic, assign) UIInterfaceOrientationMask supportedOrientations;
  58. @end
  59. @interface UGCKitRecordViewController()
  60. <TXUGCRecordListener, UIGestureRecognizerDelegate,
  61. MPMediaPickerControllerDelegate,TCBGMControllerListener,TXVideoJoinerListener,
  62. UGCKitVideoRecordMusicViewDelegate, UGCKitAudioEffectPanelDelegate, BeautyLoadPituDelegate
  63. #if POD_PITU
  64. , MCCameraDynamicDelegate
  65. #endif
  66. >
  67. {
  68. UGCKitRecordConfig *_config; // 录制配置
  69. UGCKitTheme *_theme; // 主题配置
  70. UGCKitRecordControlView *_controlView; // 界面控件覆盖层
  71. RecordState _recordState; // 录制状态
  72. NSString *_coverPath; // 保存路径, 视频为<_coverPath>.mp4结尾, 封面图为<_coverPath>.png
  73. BOOL _isFrontCamera;
  74. BOOL _vBeautyShow;
  75. BOOL _preloadingVideos;
  76. // Chorus
  77. TXVideoBeautyStyle _beautyStyle;
  78. float _beautyDepth;
  79. float _whitenDepth;
  80. float _ruddinessDepth;
  81. float _eye_level;
  82. float _face_level;
  83. BOOL _isCameraPreviewOn;
  84. // BOOL _videoRecording;
  85. CGFloat _currentRecordTime;
  86. UGCKitAudioEffectPanel *_soundMixView;
  87. BOOL _navigationBarHidden;
  88. BOOL _appForeground;
  89. // BOOL _isPaused;
  90. // 倒计时
  91. UILabel *_countDownLabel;
  92. UIView *_countDownView;
  93. NSTimer *_countDownTimer;
  94. #if POD_PITU
  95. MCCameraDynamicView *_tmplBar;
  96. NSString *_materialID;
  97. #else
  98. UIView *_tmplBar;
  99. #endif
  100. UGCKitBGMListViewController* _bgmListVC;
  101. NSObject* _BGMPath;
  102. CGFloat _BGMDuration;
  103. CGFloat _recordTime;
  104. int _deleteCount;
  105. float _zoom;
  106. BOOL _isBackDelete;
  107. BOOL _isFlash;
  108. UGCKitVideoRecordMusicView * _musicView;
  109. SpeedMode _speedMode;
  110. TCBeautyPanel * _vBeauty;
  111. UGCKitProgressHUD* _hud;
  112. CGFloat _bgmBeginTime;
  113. BOOL _bgmRecording;
  114. TXVideoJoiner * _videoJoiner;
  115. NSString * _joinVideoPath;
  116. TXVideoVoiceChangerType _voiceChangerType; // 变声参数
  117. TXVideoReverbType _revertType; // 混音参数
  118. }
  119. @property (strong, nonatomic) IBOutlet UIButton *btnNext;
  120. @property (strong, nonatomic) IBOutlet UIView *captureModeView;
  121. @property (strong, nonatomic) IBOutlet UIButton *btnChangeVideo;
  122. @property (assign, nonatomic) CaptureMode captureMode;
  123. @property (strong, nonatomic) UGCKitRecordPreviewController *previewController;
  124. @end
  125. @implementation UGCKitRecordViewController
  126. - (instancetype)initWithConfig:(UGCKitRecordConfig *)config theme:(UGCKitTheme *)theme
  127. {
  128. if (self = [super initWithNibName:nil bundle:nil]) {
  129. _config = config ?: [[UGCKitRecordConfig alloc] init];
  130. _theme = theme ?: [UGCKitTheme sharedTheme];
  131. _preloadingVideos = _config.recoverDraft;
  132. [[TXUGCRecord shareInstance] setAspectRatio:_config.ratio];
  133. _coverPath = [NSTemporaryDirectory() stringByAppendingString:[[NSUUID UUID] UUIDString]];
  134. [self _commonInit];
  135. }
  136. return self;
  137. }
  138. -(instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
  139. {
  140. self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
  141. if (self)
  142. {
  143. _config = [[UGCKitRecordConfig alloc] init];
  144. _theme = [UGCKitTheme sharedTheme];
  145. [self _commonInit];
  146. }
  147. return self;
  148. }
  149. - (void)_commonInit {
  150. _appForeground = YES;
  151. _isFrontCamera = YES;
  152. _vBeautyShow = NO;
  153. _beautyStyle = VIDOE_BEAUTY_STYLE_SMOOTH;
  154. _beautyDepth = 6.3;
  155. _whitenDepth = 2.7;
  156. _isCameraPreviewOn = NO;
  157. _recordState = RecordStateStopped;
  158. _currentRecordTime = 0;
  159. _speedMode = SpeedMode_Standard;
  160. _voiceChangerType = VIDOE_VOICECHANGER_TYPE_0; // 无变声
  161. _revertType = VIDOE_REVERB_TYPE_0; // 无混音效果
  162. [TXUGCRecord shareInstance].recordDelegate = self;
  163. _bgmListVC = [[UGCKitBGMListViewController alloc] initWithTheme:_theme];
  164. [_bgmListVC setBGMControllerListener:self];
  165. _joinVideoPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"outputJoin.mp4"];
  166. self.captureMode = CaptureModeTap;
  167. }
  168. - (void)didReceiveMemoryWarning
  169. {
  170. [super didReceiveMemoryWarning];
  171. }
  172. -(void)viewDidLoad
  173. {
  174. [super viewDidLoad];
  175. [self initUI];
  176. [self initBeautyUI];
  177. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppWillResignActive:) name:UIApplicationWillResignActiveNotification object:nil];
  178. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
  179. [[NSNotificationCenter defaultCenter] addObserver:self
  180. selector:@selector(onAudioSessionEvent:)
  181. name:AVAudioSessionInterruptionNotification
  182. object:nil];
  183. }
  184. - (void)viewWillAppear:(BOOL)animated{
  185. [super viewWillAppear:animated];
  186. _navigationBarHidden = self.navigationController.navigationBar.hidden;
  187. self.navigationController.navigationBar.hidden = YES;
  188. if (_isCameraPreviewOn == NO) {
  189. [self startCameraPreview];
  190. }else{
  191. //停止特效的声音
  192. [[[TXUGCRecord shareInstance] getBeautyManager] setMotionMute:NO];
  193. }
  194. // 恢复变声与混音效果
  195. if (_voiceChangerType >= 0) {
  196. [[TXUGCRecord shareInstance] setVoiceChangerType:_voiceChangerType];
  197. }
  198. if (_revertType >= 0) {
  199. [[TXUGCRecord shareInstance] setReverbType:_revertType];
  200. }
  201. }
  202. - (void)viewWillDisappear:(BOOL)animated
  203. {
  204. [super viewWillDisappear:animated];
  205. self.navigationController.navigationBar.hidden = _navigationBarHidden;
  206. }
  207. - (void)viewDidDisappear:(BOOL)animated
  208. {
  209. [super viewDidDisappear:animated];
  210. [self stopCameraPreview];
  211. }
  212. - (UIStatusBarStyle)preferredStatusBarStyle
  213. {
  214. return UIStatusBarStyleLightContent;
  215. }
  216. #pragma mark - Notification Handler
  217. - (void)onAudioSessionEvent:(NSNotification*)notification
  218. {
  219. NSDictionary *info = notification.userInfo;
  220. AVAudioSessionInterruptionType type = [info[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
  221. if (type == AVAudioSessionInterruptionTypeBegan) {
  222. // 在10.3及以上的系统上,分享跳其它app后再回来会收到AVAudioSessionInterruptionWasSuspendedKey的通知,不处理这个事件。
  223. if ([info objectForKey:@"AVAudioSessionInterruptionWasSuspendedKey"]) {
  224. return;
  225. }
  226. _appForeground = NO;
  227. if (_recordState == RecordStateRecording) {
  228. [self pauseRecord];
  229. }
  230. }else{
  231. AVAudioSessionInterruptionOptions options = [info[AVAudioSessionInterruptionOptionKey] unsignedIntegerValue];
  232. if (options == AVAudioSessionInterruptionOptionShouldResume) {
  233. _appForeground = YES;
  234. }
  235. }
  236. }
  237. - (void)onAppWillResignActive:(NSNotification*)notification
  238. {
  239. _appForeground = NO;
  240. if (_recordState == RecordStateRecording) {
  241. [self pauseRecord];
  242. }
  243. }
  244. - (void)onAppDidBecomeActive:(NSNotification*)notification
  245. {
  246. [[TXUGCRecord shareInstance] resumeAudioSession];
  247. _appForeground = YES;
  248. }
  249. #pragma mark - UI Setup
  250. -(void)initUI
  251. {
  252. self.title = @"";
  253. self.view.backgroundColor = _theme.backgroundColor;
  254. if (0 == _config.chorusVideos.count
  255. && UGCKitRecordStyleRecord != _config.recordStyle) {
  256. _config.recordStyle = UGCKitRecordStyleRecord;
  257. }
  258. else if (_config.chorusVideos.count < 2
  259. && UGCKitRecordStyleTrio == _config.recordStyle) {
  260. _config.chorusVideos = @[_config.chorusVideos.firstObject,
  261. _config.chorusVideos.firstObject];
  262. }
  263. // 视频界面
  264. UIView *videoContainer = [[UIView alloc] initWithFrame:self.view.bounds];
  265. videoContainer.autoresizingMask = UIViewAutoresizingFlexibleWidth
  266. | UIViewAutoresizingFlexibleHeight;
  267. [self.view addSubview:videoContainer];
  268. NSString *recordVideoPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"outputRecord.mp4"];
  269. _previewController = [[UGCKitRecordPreviewController alloc] initWithContainerView:videoContainer
  270. recordStyle:_config.recordStyle
  271. chorusVideos:_config.chorusVideos
  272. recordVideo:recordVideoPath];
  273. CGFloat top = [UIApplication sharedApplication].statusBarFrame.size.height + 25;
  274. CGFloat centerY = top;
  275. UIButton *(^allocButtonBlock)(UIImage *, SEL, NSString *) = ^(UIImage *bgImage, SEL action, NSString *title) {
  276. UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
  277. button.bounds = CGRectMake(0, 0, BUTTON_CONTROL_SIZE, BUTTON_CONTROL_SIZE);
  278. button.titleLabel.font = [UIFont systemFontOfSize:12];
  279. [button addTarget:self action:action forControlEvents:UIControlEventTouchUpInside];
  280. [button setTitleColor:self->_theme.titleColor forState:UIControlStateNormal];
  281. [button setBackgroundImage:bgImage forState:UIControlStateNormal];
  282. [button setTitle:title forState:UIControlStateNormal];
  283. return button;
  284. };
  285. // “下一步”按钮
  286. _btnNext = allocButtonBlock(_theme.nextIcon, @selector(onNext:), [_theme localizedString:@"UGCKit.Common.Next"]);
  287. _btnNext.hidden = YES;
  288. [_btnNext sizeToFit];
  289. _btnNext.center = CGPointMake(CGRectGetWidth(self.view.bounds) - 10 - CGRectGetWidth(_btnNext.frame) / 2 , centerY);
  290. [self.view addSubview:_btnNext];
  291. // "更换视频"按钮
  292. _btnChangeVideo = allocButtonBlock(_theme.editChooseVideoIcon, @selector(onChangeVideo:), [_theme localizedString:@"UGCKit.Common.Choose"]);
  293. _btnChangeVideo.bounds = CGRectMake(0, 0, 70, 30);
  294. _btnChangeVideo.center = _btnNext.center;
  295. _btnChangeVideo.hidden = YES;
  296. [self.view addSubview:_btnChangeVideo];
  297. // 主界面控件浮层
  298. _controlView = [[UGCKitRecordControlView alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(_btnNext.frame)+30,
  299. CGRectGetWidth(self.view.bounds),
  300. CGRectGetHeight(self.view.bounds) - CGRectGetMaxY(_btnNext.frame)-30)
  301. minDuration:_config.minDuration
  302. maxDuration:_config.maxDuration];
  303. _controlView.theme = _theme;
  304. _controlView.speedControlEnabled = UGCKitRecordStyleRecord == _config.recordStyle;
  305. #if UGC_SMART
  306. _controlView.musicButtonEnabled = NO;
  307. _controlView.speedControlEnabled = NO;
  308. #endif
  309. _controlView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  310. [_controlView setupViews];
  311. [self.view addSubview:_controlView];
  312. // 后退按钮
  313. UIButton *btnBack = [UGCKitSmallButton buttonWithType:UIButtonTypeCustom];
  314. btnBack.bounds = CGRectMake(0, 0, BUTTON_CONTROL_SIZE, BUTTON_CONTROL_SIZE);
  315. btnBack.center = CGPointMake(17, centerY);
  316. [btnBack setImage:_theme.backIcon forState:UIControlStateNormal];
  317. [btnBack addTarget:self action:@selector(onTapBackButton:) forControlEvents:UIControlEventTouchUpInside];
  318. [self.view addSubview:btnBack];
  319. // 比例按钮配置
  320. _controlView.videoRatio = _config.ratio;
  321. // 事件绑定
  322. [_controlView.btnMusic addTarget:self action:@selector(onBtnMusicClicked:) forControlEvents:UIControlEventTouchUpInside];
  323. [_controlView.btnRatioGroup addTarget:self action:@selector(onBtnRatioClicked:) forControlEvents:UIControlEventValueChanged];
  324. [_controlView.btnBeauty addTarget:self action:@selector(onBtnBeautyClicked:) forControlEvents:UIControlEventTouchUpInside];
  325. [_controlView.btnAudioEffect addTarget:self action:@selector(onBtnAudioMix:) forControlEvents:UIControlEventTouchUpInside];
  326. [_controlView.btnStartRecord addTarget:self action:@selector(onRecordButtonTouchDown:) forControlEvents:UIControlEventTouchDown];
  327. [_controlView.btnStartRecord addTarget:self action:@selector(onRecordButtonTouchUp:) forControlEvents:UIControlEventTouchUpInside|UIControlEventTouchUpOutside];
  328. [_controlView.btnFlash addTarget:self action:@selector(onTapTorchButton) forControlEvents:UIControlEventTouchUpInside];
  329. [_controlView.btnCamera addTarget:self action:@selector(onTapCameraSwitch) forControlEvents:UIControlEventTouchUpInside];
  330. [_controlView.btnDelete addTarget:self action:@selector(onBtnDeleteClicked) forControlEvents:UIControlEventTouchUpInside];
  331. [_controlView.btnCountDown addTarget:self action:@selector(onCountDown:) forControlEvents:UIControlEventTouchUpInside];
  332. for (UIButton *button in _controlView.speedBtnList) {
  333. [button addTarget:self action:@selector(onBtnSpeedClicked:) forControlEvents:UIControlEventTouchUpInside];
  334. }
  335. [_controlView.recordButtonSwitchControl addTarget:self action:@selector(onTapCaptureMode:) forControlEvents:UIControlEventValueChanged];
  336. UIPinchGestureRecognizer* pinchGensture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(onPInchZoom:)];
  337. [self.view addGestureRecognizer:pinchGensture];
  338. // 滑动滤镜
  339. UIPanGestureRecognizer* panGensture = [[UIPanGestureRecognizer alloc] initWithTarget:self action: @selector (onPanSlideFilter:)];
  340. panGensture.delegate = self;
  341. [self.view addGestureRecognizer:panGensture];
  342. if (UGCKitRecordStyleDuet == _config.recordStyle) { // 分屏合拍
  343. videoContainer.frame = CGRectMake(0, CGRectGetMaxY(_btnNext.frame) + 20,
  344. CGRectGetWidth(self.view.bounds),
  345. CGRectGetHeight(self.view.bounds) / 2);
  346. }
  347. [self refreshChrousUI];
  348. [self refreshRecordDuration];
  349. }
  350. - (void)refreshChrousUI
  351. {
  352. if (UGCKitRecordStyleRecord == _config.recordStyle) return;
  353. // 合唱
  354. _controlView.speedControlEnabled = NO;
  355. _controlView.photoModeEnabled = NO;
  356. _controlView.progressView.minimumTimeTipHidden = YES;
  357. _controlView.btnRatioGroup.hidden = YES;
  358. _controlView.btnMusic.hidden = YES;
  359. _controlView.btnAudioEffect.hidden = YES;
  360. _controlView.btnBeauty.frame = _controlView.btnMusic.frame;
  361. CGRect countDownFrame = _controlView.btnCountDown.frame;
  362. countDownFrame.origin.y = CGRectGetMinY(_controlView.btnRatioGroup.frame);
  363. _controlView.btnCountDown.frame = countDownFrame;
  364. _controlView.countDownModeEnabled = YES;
  365. _btnChangeVideo.hidden = NO;
  366. //用于模仿视频和录制视频的合成
  367. _videoJoiner = [[TXVideoJoiner alloc] initWithPreview:nil];
  368. _videoJoiner.joinerDelegate = self;
  369. }
  370. - (void)refreshRecordDuration
  371. {
  372. if (UGCKitRecordStyleRecord == _config.recordStyle) return;
  373. TXVideoInfo *info = nil;
  374. for (NSString *videoPath in _config.chorusVideos) {
  375. TXVideoInfo *videoInfo = [TXVideoInfoReader getVideoInfo:videoPath];
  376. if (!info || videoInfo.duration < info.duration) {
  377. info = videoInfo;
  378. }
  379. }
  380. _config.minDuration = info.duration;
  381. _config.maxDuration = info.duration;
  382. [_controlView setMinDuration:info.duration maxDuration:info.duration];
  383. }
  384. #pragma mark - UI LazyLoaders
  385. - (UGCKitAudioEffectPanel *)soundMixView {
  386. if (_soundMixView) {
  387. return _soundMixView;
  388. }
  389. _soundMixView = [[UGCKitAudioEffectPanel alloc] initWithTheme:_theme
  390. frame:CGRectMake(0, CGRectGetHeight(self.view.bounds)-AudioEffectViewHeight,
  391. CGRectGetWidth(self.view.bounds), AudioEffectViewHeight)];
  392. _soundMixView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
  393. _soundMixView.delegate = self;
  394. [self.view addSubview:_soundMixView];
  395. _soundMixView.hidden = YES;
  396. return _soundMixView;
  397. }
  398. - (UGCKitVideoRecordMusicView *)musicView {
  399. if (_musicView) {
  400. return _musicView;
  401. }
  402. _musicView = [[UGCKitVideoRecordMusicView alloc] initWithFrame:CGRectMake(0, self.view.ugckit_bottom - 330 * kScaleY, self.view.ugckit_width, 330 * kScaleY) needEffect:YES theme:_theme];
  403. _musicView.delegate = self;
  404. _musicView.hidden = YES;
  405. [self.view addSubview:_musicView];
  406. _musicView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
  407. return _musicView;
  408. }
  409. #pragma mark ---- Video Beauty UI ----
  410. -(void)initBeautyUI
  411. {
  412. NSUInteger controlHeight = [ TCBeautyPanel getHeight];
  413. CGFloat offset = 0;
  414. if (@available(iOS 11, *)) {
  415. offset = [UIApplication sharedApplication].keyWindow.safeAreaInsets.bottom;
  416. }
  417. _vBeauty = [TCBeautyPanel beautyPanelWithFrame:CGRectMake(0, self.view.frame.size.height - controlHeight - offset,
  418. self.view.frame.size.width, controlHeight)
  419. theme:_theme
  420. SDKObject:[TXUGCRecord shareInstance]];
  421. _vBeauty.hidden = YES;
  422. _vBeauty.pituDelegate = self;
  423. _vBeauty.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
  424. [self.view addSubview:_vBeauty];
  425. }
  426. - (void)setSelectedSpeed:(SpeedMode)tag
  427. {
  428. [_controlView setSelectedSpeed:tag];
  429. }
  430. -(void)setSpeedBtnHidden:(BOOL)hidden{
  431. if (UGCKitRecordStyleRecord != _config.recordStyle) hidden = YES;
  432. _controlView.speedControlEnabled = !hidden;
  433. }
  434. #pragma mark - Actions
  435. - (void)takePhoto {
  436. [[TXUGCRecord shareInstance] snapshot:^(UIImage *image) {
  437. dispatch_async(dispatch_get_main_queue(), ^{
  438. UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
  439. UIImageWriteToSavedPhotosAlbum(image, self, @selector(image:didFinishSavingWithError:contextInfo:), (__bridge void*)imageView);
  440. imageView.contentMode = UIViewContentModeScaleAspectFit;
  441. imageView.frame = self.view.bounds;
  442. [self.view insertSubview:imageView belowSubview:self->_controlView.bottomMask];
  443. CGAffineTransform t = CGAffineTransformMakeScale(0.33, 0.33);
  444. [UIView animateWithDuration:0.3 animations:^{
  445. imageView.transform = t;
  446. } completion:nil];
  447. });
  448. }];
  449. }
  450. -(IBAction)onTapBackButton:(id)sender
  451. {
  452. __weak __typeof(self) wself = self;
  453. [self pauseRecord:^{
  454. [wself _goBack];
  455. }];
  456. }
  457. - (void)_goBack {
  458. NSArray *videoPaths = [[TXUGCRecord shareInstance].partsManager getVideoPathList];
  459. if (videoPaths.count > 0) {
  460. UIAlertController *controller = [UIAlertController alertControllerWithTitle:[_theme localizedString:@"UGCKit.Record.AbandonRecord"]
  461. message:nil
  462. preferredStyle:UIAlertControllerStyleAlert];
  463. [controller addAction:[UIAlertAction actionWithTitle:[_theme localizedString:@"UGCKit.Common.OK"]
  464. style:UIAlertActionStyleDefault
  465. handler:^(UIAlertAction * _Nonnull action) {
  466. [[NSUserDefaults standardUserDefaults] setObject:nil forKey:CACHE_PATH_LIST];
  467. if (self.completion) {
  468. self.completion([UGCKitResult cancelledResult]);
  469. }
  470. }]];
  471. [controller addAction:[UIAlertAction actionWithTitle:[_theme localizedString:@"UGCKit.Common.Cancel"] style:UIAlertActionStyleCancel handler:nil]];
  472. [self presentViewController:controller animated:YES completion:nil];
  473. }else{
  474. [[NSUserDefaults standardUserDefaults] setObject:nil forKey:CACHE_PATH_LIST];
  475. if (self.completion) {
  476. self.completion([UGCKitResult cancelledResult]);
  477. }
  478. }
  479. }
  480. -(IBAction)onTapTorchButton
  481. {
  482. _isFlash = !_isFlash;
  483. _controlView.torchOn = _isFlash;
  484. [[TXUGCRecord shareInstance] toggleTorch:_isFlash];
  485. }
  486. - (IBAction)onNext:(UIButton *)sender
  487. {
  488. self.captureModeView.userInteractionEnabled = YES;
  489. _controlView.btnCountDown.enabled = YES;
  490. _controlView.recordButtonSwitchControl.enabled = YES;
  491. if (_captureMode != CaptureModePress) {
  492. [_controlView setRecordButtonStyle:UGCKitRecordButtonStyleRecord];
  493. }
  494. _controlView.controlButtonsHidden = NO;
  495. if (UGCKitRecordStyleRecord != _config.recordStyle) {
  496. _controlView.btnMusic.hidden = YES;
  497. _controlView.btnRatioGroup.hidden = YES;
  498. _controlView.btnAudioEffect.hidden = YES;
  499. }
  500. [self _finishRecord];
  501. }
  502. - (void)_finishRecord {
  503. UIButton *nextButton = _btnNext;
  504. nextButton.hidden = YES;
  505. [self stopRecordAndComplete:^(int result){
  506. nextButton.hidden = NO;
  507. }];
  508. }
  509. - (IBAction)onChangeVideo:(UIButton *)sender
  510. {
  511. UGCKitMediaPickerConfig *config = [[UGCKitMediaPickerConfig alloc] init];
  512. config.mediaType = UGCKitMediaTypeVideo;
  513. config.maxItemCount = 1;
  514. UGCKitMediaPickerViewController *pickerController = [[UGCKitMediaPickerViewController alloc] initWithConfig:config theme:_theme];
  515. UGCKitNavControllerPrivate *navController = [[UGCKitNavControllerPrivate alloc] initWithRootViewController:pickerController];
  516. navController.supportedOrientations = self.supportedInterfaceOrientations;
  517. navController.modalPresentationStyle = UIModalPresentationFullScreen;
  518. WEAKIFY(self);
  519. pickerController.completion = ^(UGCKitResult *result) {
  520. if (!result.cancelled && result.code == 0) {
  521. [weak_self doChangeVideo:result];
  522. } else {
  523. NSLog(@"isCancelled: %c, failed: %@", result.cancelled ? 'y' : 'n', result.info[NSLocalizedDescriptionKey]);
  524. }
  525. [weak_self dismissViewControllerAnimated:YES completion:nil];
  526. };
  527. [self presentViewController:navController animated:YES completion:NULL];
  528. }
  529. - (void)doChangeVideo:(UGCKitResult *)result
  530. {
  531. if (![result.media.videoAsset isKindOfClass:[AVURLAsset class]]) return;
  532. NSString *videoPath = ((AVURLAsset *)result.media.videoAsset).URL.path;
  533. if (!videoPath) {
  534. videoPath = @"";
  535. }
  536. [self.previewController changeChorusVideo:videoPath];
  537. if (UGCKitRecordStyleDuet == _config.recordStyle) {
  538. _config.chorusVideos = @[videoPath];
  539. } else if (UGCKitRecordStyleTrio == _config.recordStyle) {
  540. _config.chorusVideos = @[videoPath, videoPath];
  541. }
  542. [self refreshRecordDuration];
  543. }
  544. - (IBAction)onBtnMusicClicked:(id)sender
  545. {
  546. _vBeauty.hidden = YES;
  547. UIView *musicView = [self musicView];
  548. if (_BGMPath) {
  549. musicView.hidden = !musicView.hidden;
  550. [self hideBottomView:!musicView.hidden];
  551. }else{
  552. UINavigationController *nv = [[UINavigationController alloc] initWithRootViewController:_bgmListVC];
  553. [nv.navigationBar setTitleTextAttributes:@{NSForegroundColorAttributeName:_theme.titleColor}];
  554. nv.navigationBar.barTintColor = _theme.backgroundColor;
  555. nv.modalPresentationStyle = UIModalPresentationFullScreen;
  556. [self presentViewController:nv animated:YES completion:nil];
  557. [_bgmListVC loadBGMList];
  558. }
  559. }
  560. - (IBAction)onBtnRatioClicked:(UGCKitSlideButton *)sender
  561. {
  562. TXVideoAspectRatio ratio = sender.buttons[sender.selectedIndex].tag;;
  563. [self _switchToRatio:ratio];
  564. }
  565. -(void)onBtnSpeedClicked:(UIButton *)btn
  566. {
  567. [UIView animateWithDuration:0.3 animations:^{
  568. self->_speedMode = btn.tag;
  569. [self setSelectedSpeed:self->_speedMode];
  570. }];
  571. }
  572. -(IBAction)onBtnDeleteClicked
  573. {
  574. if (_recordState == RecordStateRecording) {
  575. [self pauseRecord];
  576. }
  577. if (0 == [TXUGCRecord shareInstance].partsManager.getVideoPathList.count) {
  578. return;
  579. }
  580. if (0 == _deleteCount) {
  581. [_controlView.progressView prepareDeletePart];
  582. }else{
  583. [_controlView.progressView comfirmDeletePart];
  584. [[TXUGCRecord shareInstance].partsManager deleteLastPart];
  585. _isBackDelete = YES;
  586. }
  587. if (2 == ++ _deleteCount) {
  588. _deleteCount = 0;
  589. }
  590. }
  591. - (IBAction)onBtnAudioMix:(id)sender {
  592. [self hideBottomView:YES];
  593. UGCKitAudioEffectPanel *soundMixView = [self soundMixView];
  594. soundMixView.hidden = NO;
  595. _vBeauty.hidden = YES;
  596. _musicView.hidden = YES;
  597. CATransition *animation = [CATransition animation];
  598. animation.type = kCATransitionFade;
  599. [soundMixView.layer addAnimation:animation forKey:nil];
  600. }
  601. - (IBAction)onCountDown:(id)sender {
  602. [self hideBottomView:YES];
  603. [self startCountDown];
  604. }
  605. -(IBAction)onBtnBeautyClicked:(id)sender
  606. {
  607. _vBeautyShow = !_vBeautyShow;
  608. _musicView.hidden = YES;
  609. _vBeauty.hidden = !_vBeautyShow;
  610. [self hideBottomView:_vBeautyShow];
  611. }
  612. -(IBAction)onTapCameraSwitch
  613. {
  614. _isFrontCamera = !_isFrontCamera;
  615. [[TXUGCRecord shareInstance] switchCamera:_isFrontCamera];
  616. _controlView.isFrontCamera = _isFrontCamera;
  617. if (_isFrontCamera) {
  618. _isFlash = NO;
  619. _controlView.torchOn = NO;
  620. }
  621. [[TXUGCRecord shareInstance] toggleTorch:_isFlash];
  622. }
  623. - (IBAction)onTapCaptureMode:(UGCKitSlideOptionControl *)control
  624. {
  625. CaptureMode mode = (CaptureMode)control.selectedIndex;
  626. [UIView animateWithDuration:0.1 animations:^{
  627. [self->_controlView setRecordButtonStyle:(UGCKitRecordButtonStyle)mode];
  628. }];
  629. self.captureMode = mode;
  630. }
  631. - (void)_configButtonToPause {
  632. _controlView.controlButtonsHidden = YES;
  633. [_controlView setRecordButtonStyle:UGCKitRecordButtonStylePause];
  634. }
  635. #pragma mark - Count Down
  636. - (void)startCountDown {
  637. if (_countDownTimer) {
  638. return;
  639. }
  640. if (_countDownView == nil) {
  641. UIVisualEffectView *view = [[UIVisualEffectView alloc] initWithEffect:[UIBlurEffect effectWithStyle: UIBlurEffectStyleExtraLight]];
  642. view.layer.cornerRadius = 20;
  643. view.clipsToBounds = YES;
  644. view.translatesAutoresizingMaskIntoConstraints = NO;
  645. UILabel *countDownLabel = [[UILabel alloc] init];
  646. countDownLabel.translatesAutoresizingMaskIntoConstraints = NO;
  647. countDownLabel.textColor = [UIColor colorWithWhite:0.33 alpha:1];
  648. countDownLabel.font = [UIFont systemFontOfSize:100];
  649. [view.contentView addSubview:countDownLabel];
  650. [view.contentView addConstraint:[NSLayoutConstraint constraintWithItem:view.contentView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:countDownLabel attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]];
  651. [view.contentView addConstraint:[NSLayoutConstraint constraintWithItem:view.contentView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:countDownLabel attribute:NSLayoutAttributeCenterY multiplier:1 constant:0]];
  652. [view addConstraint:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:150]];
  653. [view addConstraint:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:150]];
  654. [self.view addSubview:view];
  655. [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]];
  656. [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeCenterY multiplier:1 constant:0]];
  657. _countDownView = view;
  658. _countDownLabel = countDownLabel;
  659. }
  660. _countDownView.hidden = NO;
  661. _countDownLabel.text = @"3";
  662. _countDownTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(onCountDownTimer:) userInfo:nil repeats:YES];
  663. _countDownView.hidden = NO;
  664. }
  665. - (void)onCountDownTimer:(NSTimer *)timer {
  666. if ([UIApplication sharedApplication].applicationState != UIApplicationStateActive) {
  667. return;
  668. }
  669. int count = _countDownLabel.text.intValue - 1;
  670. _countDownLabel.text = @(count).stringValue;
  671. if (count == 0) {
  672. [_countDownTimer invalidate];
  673. _countDownTimer = nil;
  674. _countDownView.hidden = YES;
  675. _controlView.recordButtonStyle = UGCKitRecordButtonStylePause;
  676. _controlView.recordButtonSwitchControl.selectedIndex = CaptureModeTap;
  677. self.captureMode = CaptureModeTap;
  678. [self startRecord];
  679. [self hideBottomView:NO];
  680. }
  681. }
  682. #pragma mark - Control Visibility
  683. #pragma mark - Properties
  684. -(void)syncSpeedRateToSDK{
  685. switch (_speedMode) {
  686. case SpeedMode_VerySlow:
  687. [[TXUGCRecord shareInstance] setRecordSpeed:VIDEO_RECORD_SPEED_SLOWEST];
  688. break;
  689. case SpeedMode_Slow:
  690. [[TXUGCRecord shareInstance] setRecordSpeed:VIDEO_RECORD_SPEED_SLOW];
  691. break;
  692. case SpeedMode_Standard:
  693. [[TXUGCRecord shareInstance] setRecordSpeed:VIDEO_RECORD_SPEED_NOMAL];
  694. break;
  695. case SpeedMode_Quick:
  696. [[TXUGCRecord shareInstance] setRecordSpeed:VIDEO_RECORD_SPEED_FAST];
  697. break;
  698. case SpeedMode_VeryQuick:
  699. [[TXUGCRecord shareInstance] setRecordSpeed:VIDEO_RECORD_SPEED_FASTEST];
  700. break;
  701. default:
  702. break;
  703. }
  704. }
  705. #pragma mark - Right Side Button Event Handler
  706. - (void)_switchToRatio:(TXVideoAspectRatio)ratio;
  707. {
  708. _config.ratio = ratio;
  709. [_controlView setVideoRatio:ratio];
  710. [[TXUGCRecord shareInstance] setAspectRatio:_config.ratio];
  711. }
  712. #pragma mark - Record Control
  713. -(void)startSDKRecord
  714. {
  715. _controlView.btnCountDown.enabled = NO;
  716. [self startCameraPreview];
  717. [self syncSpeedRateToSDK];
  718. int result = [[TXUGCRecord shareInstance] startRecord:[_coverPath stringByAppendingString:@".mp4"]
  719. coverPath:[_coverPath stringByAppendingString:@".png"]];
  720. [UGCKitReporter report:UGCKitReportItem_startrecord userName:nil code:result msg:result == 0 ? ASLocalizedString(@"启动录制成功"): ASLocalizedString(@"启动录制失败")];
  721. if(0 != result)
  722. {
  723. if(-3 == result) [self alert:[_theme localizedString:@"UGCKit.Record.HintLaunchRecordFailed"] msg:[_theme localizedString:@"UGCKit.Record.ErrorCamera"]];
  724. if(-4 == result) [self alert:[_theme localizedString:@"UGCKit.Record.HintLaunchRecordFailed"] msg:[_theme localizedString:@"UGCKit.Record.ErrorMIC"]];
  725. if(-5 == result) [self alert:[_theme localizedString:@"UGCKit.Record.HintLaunchRecordFailed"] msg:[_theme localizedString:@"UGCKit.Record.ErrorLicense"]];
  726. }else{
  727. //如果设置了BGM,播放BGM
  728. [self playBGM:_bgmBeginTime];
  729. //初始化录制状态
  730. _recordState = RecordStateRecording;
  731. _bgmRecording = YES;
  732. _controlView.recordButtonSwitchControl.enabled = NO;
  733. //录制过程中不能切换分辨率,不能切换拍照模式
  734. _controlView.btnRatioGroup.enabled = NO;
  735. [_controlView.btnRatioGroup shrink];
  736. self.captureModeView.userInteractionEnabled = NO;
  737. [self setSpeedBtnHidden:YES];
  738. [self _configButtonToPause];
  739. if (UGCKitRecordStyleRecord != _config.recordStyle) {
  740. [self.previewController startPlayChorusVideos:_recordTime
  741. toTime:_config.maxDuration];
  742. }
  743. }
  744. }
  745. - (void)_pauseAndAddMark:(void(^)(void))completion {
  746. NSUInteger countBefore = [TXUGCRecord shareInstance].partsManager.getVideoPathList.count;
  747. UGCKitRecordControlView *controlView = _controlView;
  748. void (^afterPause)(void)= ^{
  749. NSUInteger count = [TXUGCRecord shareInstance].partsManager.getVideoPathList.count;
  750. if (count != countBefore) {
  751. [controlView.progressView pause];
  752. }
  753. if (completion) {
  754. completion();
  755. }
  756. };
  757. if ([[TXUGCRecord shareInstance] pauseRecord:afterPause] < 0) {
  758. afterPause();
  759. }
  760. }
  761. - (void)pauseRecord {
  762. [self pauseRecord:nil];
  763. }
  764. - (void)pauseRecord:(void(^)(void))completion {
  765. self.captureModeView.userInteractionEnabled = YES;
  766. _controlView.btnCountDown.enabled = YES;
  767. _controlView.recordButtonSwitchControl.enabled = YES;
  768. __weak __typeof(self) weakSelf = self;
  769. UGCKitRecordControlView *controlView = _controlView;
  770. controlView.btnStartRecord.enabled = NO;
  771. [self _pauseAndAddMark:^{
  772. [weakSelf saveVideoClipPathToPlist];
  773. controlView.btnStartRecord.enabled = YES;
  774. if (completion) {
  775. completion();
  776. }
  777. }];
  778. [self pauseBGM];
  779. if (_captureMode != CaptureModePress) {
  780. [_controlView setRecordButtonStyle:UGCKitRecordButtonStyleRecord];
  781. }
  782. _controlView.controlButtonsHidden = NO;
  783. if (UGCKitRecordStyleRecord != _config.recordStyle) {
  784. _controlView.btnMusic.hidden = YES;
  785. _controlView.btnRatioGroup.hidden = YES;
  786. _controlView.btnAudioEffect.hidden = YES;
  787. }
  788. [self setSpeedBtnHidden:NO];
  789. _recordState = RecordStatePaused;
  790. [self.previewController stopPlayChorusVideos];
  791. }
  792. - (void)startRecord {
  793. if (_recordTime >= _config.maxDuration) {
  794. // 已经录制到最大时长
  795. return;
  796. }
  797. if (_recordState == RecordStateStopped) {
  798. [self startSDKRecord];
  799. return;
  800. }
  801. _controlView.btnCountDown.enabled = NO;
  802. self.captureModeView.userInteractionEnabled = NO;
  803. [self syncSpeedRateToSDK];
  804. if (_bgmRecording) {
  805. [self resumeBGM];
  806. }else{
  807. [self playBGM:_bgmBeginTime];
  808. _bgmRecording = YES;
  809. }
  810. [[TXUGCRecord shareInstance] resumeRecord];
  811. [self _configButtonToPause];
  812. if (_deleteCount == 1) {
  813. [_controlView.progressView cancelDelete];
  814. _deleteCount = 0;
  815. }
  816. [self setSpeedBtnHidden:YES];
  817. _controlView.recordButtonSwitchControl.enabled = NO;
  818. _recordState = RecordStateRecording;
  819. [self.previewController startPlayChorusVideos:_recordTime
  820. toTime:_config.maxDuration];
  821. }
  822. -(void)stopRecordAndComplete:(void(^)(int))completion
  823. {
  824. _btnNext.hidden = YES;
  825. _controlView.btnCountDown.enabled = YES;
  826. _controlView.recordButtonSwitchControl.enabled = YES;
  827. [_controlView setRecordButtonStyle:UGCKitRecordButtonStyleRecord];
  828. [self setSpeedBtnHidden:NO];
  829. [self.previewController stopPlayChorusVideos];
  830. //调用partsManager快速合成视频,不破坏录制状态,下次返回后可以接着录制(注意需要先暂停视频录制)
  831. __weak __typeof(self) weakSelf = self;
  832. if (_recordState == RecordStateRecording) {
  833. [self _pauseAndAddMark:^{
  834. __strong __typeof(weakSelf) self = weakSelf; if (self == nil) { return; }
  835. self->_recordState = RecordStatePaused;
  836. [self saveVideoClipPathToPlist];
  837. [[TXUGCRecord shareInstance].partsManager joinAllParts:self.previewController.recordVideoPath
  838. complete:^(int result) {
  839. if (completion){
  840. completion(result);
  841. }
  842. [weakSelf onFinishRecord:result];
  843. }];
  844. }];
  845. } else {
  846. [[TXUGCRecord shareInstance].partsManager joinAllParts:self.previewController.recordVideoPath
  847. complete:^(int result) {
  848. if (completion){
  849. completion(result);
  850. }
  851. [weakSelf onFinishRecord:result];
  852. }];
  853. }
  854. }
  855. #pragma mark - Camera Control
  856. - (void)onPInchZoom:(UIPinchGestureRecognizer*)recognizer
  857. {
  858. if (recognizer.state == UIGestureRecognizerStateBegan || recognizer.state == UIGestureRecognizerStateChanged) {
  859. [[TXUGCRecord shareInstance] setZoom:MIN(MAX(1.0, _zoom * recognizer.scale),5.0)];
  860. }else if (recognizer.state == UIGestureRecognizerStateEnded){
  861. _zoom = MIN(MAX(1.0, _zoom * recognizer.scale),5.0);
  862. recognizer.scale = 1;
  863. }
  864. }
  865. #pragma mark - Record Button Actions
  866. - (IBAction)onRecordButtonTouchUp:(id)sender
  867. {
  868. if (self.captureMode == CaptureModePress) {
  869. [self pauseRecord];
  870. } else if (self.captureMode == CaptureModeTap) {
  871. if (_recordState == RecordStateRecording) {
  872. [self pauseRecord];
  873. } else {
  874. [self startRecord];
  875. }
  876. }
  877. }
  878. // 录制按钮按下
  879. - (void)onRecordButtonTouchDown:(id)sender {
  880. if (self.captureMode == CaptureModeStill) {
  881. [self takePhoto];
  882. } else if (self.captureMode == CaptureModePress) {
  883. [self startRecord];
  884. }
  885. }
  886. - (void)changeCaptureModeUI:(CaptureMode)captureMode
  887. {
  888. self.captureMode = captureMode;
  889. _controlView.recordButtonSwitchControl.selectedIndex = captureMode;
  890. }
  891. - (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
  892. {
  893. UIImageView *imageView = (__bridge UIImageView *)contextInfo;
  894. [UIView animateWithDuration:0.3
  895. delay:0
  896. options:UIViewAnimationOptionCurveLinear|UIViewAnimationOptionBeginFromCurrentState
  897. animations:^{
  898. CGAffineTransform t = CGAffineTransformTranslate(imageView.transform, 0, self.view.ugckit_height);
  899. imageView.transform = CGAffineTransformScale(t, 0.5, 0.5);
  900. } completion:^(BOOL finished) {
  901. [imageView removeFromSuperview];
  902. }];
  903. }
  904. - (BOOL)shouldAutorotate {
  905. return YES;
  906. }
  907. - (UIInterfaceOrientationMask)supportedInterfaceOrientations {
  908. return UIInterfaceOrientationMaskPortrait;
  909. }
  910. -(void)startCameraPreview
  911. {
  912. if (_isCameraPreviewOn == NO)
  913. {
  914. //简单设置
  915. // TXUGCSimpleConfig * param = [[TXUGCSimpleConfig alloc] init];
  916. // param.videoQuality = VIDEO_QUALITY_MEDIUM;
  917. // [[TXUGCRecord shareInstance] startCameraSimple:param preview:_previewController.videoRecordView];
  918. //自定义设置
  919. TXUGCCustomConfig * param = [[TXUGCCustomConfig alloc] init];
  920. param.videoResolution = _config.resolution;
  921. param.videoFPS = _config.fps;
  922. param.videoBitratePIN = _config.videoBitrate;
  923. param.GOP = _config.gop;
  924. param.audioSampleRate = _config.audioSampleRate;
  925. param.minDuration = _config.minDuration;
  926. param.maxDuration = _config.maxDuration + 2;
  927. param.frontCamera = _isFrontCamera;
  928. param.enableAEC = _config.AECEnabled;
  929. [[TXUGCRecord shareInstance] startCameraCustom:param preview:_previewController.videoRecordView];
  930. TXBeautyManager *beautyManager = [[TXUGCRecord shareInstance] getBeautyManager];
  931. [beautyManager setBeautyStyle:(TXBeautyStyle)_beautyStyle];
  932. [beautyManager setBeautyLevel:_beautyDepth];
  933. [beautyManager setWhitenessLevel:_whitenDepth];
  934. [beautyManager setRuddyLevel:_ruddinessDepth];
  935. if (UGCKitRecordStyleRecord == _config.recordStyle) {
  936. [[TXUGCRecord shareInstance] setVideoRenderMode:VIDEO_RENDER_MODE_ADJUST_RESOLUTION];
  937. } else {
  938. [[TXUGCRecord shareInstance] setVideoRenderMode:VIDEO_RENDER_MODE_FULL_FILL_SCREEN];
  939. }
  940. [beautyManager setEyeScaleLevel:_eye_level];
  941. [beautyManager setFaceSlimLevel:_face_level];
  942. if (_config.watermark) {
  943. UIImage *watermark = _config.watermark.image;
  944. CGRect watermarkFrame = _config.watermark.frame;
  945. [[TXUGCRecord shareInstance] setWaterMark:watermark normalizationFrame:watermarkFrame];
  946. } else {
  947. [[TXUGCRecord shareInstance] setWaterMark:nil normalizationFrame:CGRectZero];;
  948. }
  949. #if POD_PITU
  950. [self motionTmplSelected:_materialID];
  951. #endif
  952. //内存里面没有视频数据,重置美颜状态
  953. if ([TXUGCRecord shareInstance].partsManager.getVideoPathList.count == 0) {
  954. [self resetBeautySettings];
  955. }
  956. //加载本地视频 -> 内存
  957. if (_preloadingVideos) {
  958. NSArray *cachePathList = [[NSUserDefaults standardUserDefaults] objectForKey:CACHE_PATH_LIST];
  959. NSString *cacheFolder = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:UGCKIT_PARTS_DIR];
  960. //预加载视频 -> SDK
  961. TXVideoInfo *videoInfo = nil;
  962. for (NSInteger i = cachePathList.count - 1; i >= 0; i --) {
  963. NSString *videoPath = [cacheFolder stringByAppendingPathComponent:cachePathList[i]];
  964. [[TXUGCRecord shareInstance].partsManager insertPart:videoPath atIndex:0];
  965. if (i == 0) {
  966. videoInfo = [TXVideoInfoReader getVideoInfo:videoPath];
  967. }
  968. }
  969. //录制分辨比例初始化
  970. if (videoInfo.width == 720 && videoInfo.height == 1280) {
  971. [self _switchToRatio:VIDEO_ASPECT_RATIO_9_16];
  972. }else if(videoInfo.width == 720 && videoInfo.height == 960) {
  973. [self _switchToRatio:VIDEO_ASPECT_RATIO_4_3];
  974. }else if(videoInfo.width == 720 && videoInfo.height == 720) {
  975. [self _switchToRatio:VIDEO_ASPECT_RATIO_1_1];
  976. }
  977. _controlView.btnRatioGroup.enabled = cachePathList.count == 0;
  978. //进度条初始化
  979. CGFloat time = 0;
  980. for (NSInteger i = 0; i < cachePathList.count; i ++) {
  981. NSString *videoPath = [cacheFolder stringByAppendingPathComponent:cachePathList[i]];
  982. time = time + [TXVideoInfoReader getVideoInfo:videoPath].duration;
  983. [_controlView.progressView pauseAtTime:time];
  984. }
  985. _preloadingVideos = NO;
  986. }
  987. _isCameraPreviewOn = YES;
  988. }
  989. }
  990. - (void)resetBeautySettings {
  991. [_vBeauty resetAndApplyValues];
  992. [[TXUGCRecord shareInstance] setFilter:nil];
  993. [[TXUGCRecord shareInstance] setGreenScreenFile:nil];
  994. }
  995. -(void)stopCameraPreview
  996. {
  997. if (_isCameraPreviewOn == YES)
  998. {
  999. [[TXUGCRecord shareInstance] stopCameraPreview];
  1000. _isCameraPreviewOn = NO;
  1001. }
  1002. }
  1003. - (void)saveVideoClipPathToPlist
  1004. {
  1005. if (UGCKitRecordStyleRecord != _config.recordStyle) {
  1006. // 合唱暂不支持草稿
  1007. return;
  1008. }
  1009. NSMutableArray *cachePathList = [NSMutableArray array];
  1010. for (NSString *videoPath in [TXUGCRecord shareInstance].partsManager.getVideoPathList) {
  1011. [cachePathList addObject:[[videoPath pathComponents] lastObject]];
  1012. }
  1013. [[NSUserDefaults standardUserDefaults] setObject:cachePathList forKey:CACHE_PATH_LIST];
  1014. [[NSUserDefaults standardUserDefaults] synchronize];
  1015. }
  1016. -(void)onFinishRecord:(int)result
  1017. {
  1018. _btnNext.hidden = NO;
  1019. if(0 == result){
  1020. if (UGCKitRecordStyleRecord == _config.recordStyle) {
  1021. [self stopCameraPreview];
  1022. if (self.completion) {
  1023. NSAssert(self.previewController.recordVideoPath != nil, @"unexpected");
  1024. UGCKitResult *result = [[UGCKitResult alloc] init];;
  1025. result.media = [UGCKitMedia mediaWithVideoPath:self.previewController.recordVideoPath];
  1026. result.coverImage = [[UIImage alloc] initWithContentsOfFile:[_coverPath stringByAppendingString:@".png"]];
  1027. if (self.completion) {
  1028. self.completion(result);
  1029. }
  1030. }
  1031. } else {
  1032. _btnNext.hidden = YES;
  1033. [self joinVideos];
  1034. }
  1035. [UGCKitReporter report:UGCKitReportItem_videorecord userName:nil code:0 msg:ASLocalizedString(@"视频录制成功")];
  1036. }else{
  1037. NSString * message = [NSString stringWithFormat:@"%@(%d)",
  1038. [_theme localizedString:@"UGCKit.Record.TryAgain"],
  1039. result];
  1040. [self alert:[_theme localizedString:@"UGCKit.Media.HintVideoSynthesizeFailed"]
  1041. msg:message];
  1042. [UGCKitReporter report:UGCKitReportItem_videorecord userName:nil code:-1 msg:ASLocalizedString(@"视频录制失败")];
  1043. }
  1044. }
  1045. - (void)joinVideos
  1046. {
  1047. NSString * recordVideoPath = self.previewController.recordVideoPath;
  1048. if (0 == recordVideoPath.length
  1049. || 0 == _config.chorusVideos.count
  1050. || ![[NSFileManager defaultManager] fileExistsAtPath:recordVideoPath]
  1051. || ![[NSFileManager defaultManager] fileExistsAtPath:_config.chorusVideos.firstObject]
  1052. || (UGCKitRecordStyleTrio == _config.recordStyle
  1053. && (_config.chorusVideos.count < 2 || ![[NSFileManager defaultManager] fileExistsAtPath:_config.chorusVideos[1]]))) {
  1054. [self alert:[_theme localizedString:@"UGCKit.Media.HintVideoSynthesizeFailed"]
  1055. msg:[_theme localizedString:@"UGCKit.Record.TryAgain"]];
  1056. return;
  1057. }
  1058. CGFloat canvasWidth = 720 * 2, canvasHeight = 1280;
  1059. CGFloat halfWidth = canvasWidth / 2;
  1060. NSArray *displayRects = @[[NSValue valueWithCGRect:CGRectMake(0, 0, halfWidth, canvasHeight)],
  1061. [NSValue valueWithCGRect:CGRectMake(halfWidth, 0, halfWidth, canvasHeight)]];
  1062. if (UGCKitRecordStyleTrio == _config.recordStyle) {
  1063. canvasWidth = 720;
  1064. CGFloat oneThirdHeight = canvasHeight / 3;
  1065. displayRects = @[[NSValue valueWithCGRect:CGRectMake(0, 0, canvasWidth, oneThirdHeight)],
  1066. [NSValue valueWithCGRect:CGRectMake(0, oneThirdHeight, canvasWidth, oneThirdHeight)],
  1067. [NSValue valueWithCGRect:CGRectMake(0, canvasHeight - oneThirdHeight, canvasWidth, oneThirdHeight)]];
  1068. }
  1069. if (0 == [_videoJoiner setVideoPathList:self.previewController.allVideoPaths]) {
  1070. [_videoJoiner setSplitScreenList:displayRects canvasWidth:canvasWidth canvasHeight:canvasHeight];
  1071. [_videoJoiner setVideoVolumes:self.previewController.allVideoVolumes];
  1072. [_videoJoiner splitJoinVideo:VIDEO_COMPRESSED_720P videoOutputPath:_joinVideoPath];
  1073. if (nil == _hud) {
  1074. _hud = [UGCKitProgressHUD showHUDAddedTo:self.view animated:YES];
  1075. } else {
  1076. [self.view addSubview:_hud];
  1077. [_hud showAnimated:YES];
  1078. }
  1079. _hud.mode = UGCKitProgressHUDModeText;
  1080. _hud.label.text = [_theme localizedString:@"UGCKit.Media.VideoSynthesizing"];
  1081. } else {
  1082. [self alert:[_theme localizedString:@"UGCKit.Media.HintVideoSynthesizeFailed"]
  1083. msg:[_theme localizedString:@"UGCKit.Record.VideoChorusNotSupported"]];
  1084. }
  1085. }
  1086. /// 选拍照模式
  1087. - (void)setCaptureMode:(CaptureMode)captureMode
  1088. {
  1089. _captureMode = captureMode;
  1090. BOOL isStillMode = captureMode == CaptureModeStill;
  1091. _controlView.speedControlEnabled = !isStillMode;
  1092. _controlView.progressView.hidden = isStillMode;
  1093. _controlView.recordTimeLabel.hidden = isStillMode;
  1094. _controlView.btnDelete.hidden = isStillMode;
  1095. }
  1096. #pragma mark Control Panel Switching
  1097. - (void) touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
  1098. {
  1099. if (_vBeautyShow)
  1100. {
  1101. UITouch *touch = [[event allTouches] anyObject];
  1102. CGPoint _touchPoint = [touch locationInView:self.view];
  1103. if (NO == CGRectContainsPoint(_vBeauty.frame, _touchPoint))
  1104. {
  1105. [self onBtnBeautyClicked:nil];
  1106. }
  1107. }
  1108. if (_musicView && !_musicView.hidden) {
  1109. CGPoint _touchPoint = [[[event allTouches] anyObject] locationInView:self.view];
  1110. if (NO == CGRectContainsPoint(_musicView.frame, _touchPoint)){
  1111. _musicView.hidden = !_musicView.hidden;
  1112. [self hideBottomView:!_musicView.hidden];
  1113. }
  1114. }
  1115. if (_soundMixView && !_soundMixView.hidden) {
  1116. _soundMixView.hidden = YES;
  1117. _controlView.bottomMask.hidden = NO;
  1118. [self hideBottomView:NO];
  1119. CATransition *animation = [CATransition animation];
  1120. animation.duration = 0.1;
  1121. animation.type = kCATransitionFade;
  1122. [_soundMixView.superview.layer addAnimation:animation forKey:nil];
  1123. }
  1124. }
  1125. - (void)hideBottomView:(BOOL)bHide
  1126. {
  1127. _controlView.hidden = bHide;
  1128. }
  1129. #pragma mark - TXUGCRecordListener
  1130. -(void) onRecordProgress:(NSInteger)milliSecond;
  1131. {
  1132. _recordTime = milliSecond / 1000.0;
  1133. BOOL shouldPause = _recordTime >= _config.maxDuration;
  1134. [self updateRecordProgressLabel: _recordTime];
  1135. BOOL isEmpty = milliSecond == 0;
  1136. //录制过程中不能切换BGM, 不能改变声音效果,不能更换合拍视频
  1137. if (UGCKitRecordStyleRecord == _config.recordStyle) {
  1138. _btnChangeVideo.hidden = YES;
  1139. } else {
  1140. _btnChangeVideo.hidden = !isEmpty;
  1141. }
  1142. _controlView.btnRatioGroup.enabled = isEmpty;
  1143. _controlView.btnMusic.enabled = isEmpty;
  1144. _btnNext.hidden = milliSecond / 1000.0 < _config.minDuration;
  1145. _controlView.btnAudioEffect.enabled = _controlView.btnMusic.enabled;
  1146. //回删之后被模仿视频进度回退
  1147. if (_isBackDelete && UGCKitRecordStyleRecord != _config.recordStyle) {
  1148. [self.previewController seekChorusVideosToTime:_recordTime];
  1149. _isBackDelete = NO;
  1150. }
  1151. if (shouldPause) {
  1152. [self pauseRecord];
  1153. if (_config.autoComplete) {
  1154. [self _finishRecord];
  1155. }
  1156. }
  1157. }
  1158. -(void) onRecordComplete:(TXUGCRecordResult*)result;
  1159. {
  1160. [_controlView setRecordButtonStyle:UGCKitRecordButtonStyleRecord];
  1161. if (_appForeground)
  1162. {
  1163. if (_currentRecordTime >= _config.minDuration)
  1164. {
  1165. if (result.retCode != UGC_RECORD_RESULT_FAILED) {
  1166. [self onFinishRecord:(int)result.retCode];
  1167. }else{
  1168. [self toastTip:[_theme localizedString:@"UGCKit.Record.ErrorREC"]];
  1169. }
  1170. } else {
  1171. [self toastTip:[_theme localizedString:@"UGCKit.Record.ErrorTime"]];
  1172. }
  1173. }
  1174. }
  1175. - (void)updateRecordProgressLabel:(CGFloat)second {
  1176. _currentRecordTime = second;
  1177. [_controlView.progressView update:_currentRecordTime / _config.maxDuration];
  1178. long min = (int)_currentRecordTime / 60;
  1179. long sec = (int)_currentRecordTime % 60;
  1180. [_controlView.recordTimeLabel setText:[NSString stringWithFormat:@"%02ld:%02ld", min, sec]];
  1181. }
  1182. #pragma mark TXVideoJoinerListener
  1183. -(void) onJoinProgress:(float)progress
  1184. {
  1185. _hud.label.text = [NSString stringWithFormat:@"%@%d%%",[_theme localizedString:@"UGCKit.Media.VideoSynthesizing"], (int)(progress * 100)];
  1186. }
  1187. -(void) onJoinComplete:(TXJoinerResult *)result
  1188. {
  1189. _btnNext.hidden = NO;
  1190. [_hud hideAnimated:YES];
  1191. if (_appForeground && result.retCode == RECORD_RESULT_OK) {
  1192. [self stopCameraPreview];
  1193. if (self.completion) {
  1194. UGCKitResult *result = [[UGCKitResult alloc] init];
  1195. result.media = [UGCKitMedia mediaWithVideoPath:_joinVideoPath];
  1196. self.completion(result);
  1197. }
  1198. }else{
  1199. [self alert:[_theme localizedString:@"UGCKit.Media.HintVideoSynthesizeFailed"] msg:result.descMsg];
  1200. }
  1201. [UGCKitReporter report:UGCKitReportItem_videojoiner userName:nil code:result.retCode msg:result.descMsg];
  1202. }
  1203. #if POD_PITU
  1204. - (void)motionTmplSelected:(NSString *)materialID {
  1205. if (materialID == nil) {
  1206. [MCTip hideText];
  1207. }
  1208. _materialID = materialID;
  1209. if ([MaterialManager isOnlinePackage:materialID]) {
  1210. [[TXUGCRecord shareInstance] selectMotionTmpl:materialID inDir:[MaterialManager packageDownloadDir]];
  1211. } else {
  1212. NSString *localPackageDir = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"Resource"];
  1213. [[TXUGCRecord shareInstance] selectMotionTmpl:materialID inDir:localPackageDir];
  1214. }
  1215. }
  1216. #endif
  1217. #pragma mark - BeautyLoadPituDelegate
  1218. - (void)onLoadPituStart
  1219. {
  1220. dispatch_async(dispatch_get_main_queue(), ^{
  1221. UGCKitProgressHUD *hud = [UGCKitProgressHUD showHUDAddedTo:self.view animated:YES];
  1222. hud.mode = UGCKitProgressHUDModeText;
  1223. hud.label.text = [self->_theme localizedString:@"UGCKit.Record.ResourceLoadBegin"];
  1224. self->_hud = hud;
  1225. });
  1226. }
  1227. - (void)onLoadPituProgress:(CGFloat)progress
  1228. {
  1229. dispatch_async(dispatch_get_main_queue(), ^{
  1230. self->_hud.label.text = [NSString stringWithFormat:[self->_theme localizedString:@"UGCKit.Record.ResourceLoading"],(int)(progress * 100)];
  1231. });
  1232. }
  1233. - (void)onLoadPituFinished
  1234. {
  1235. dispatch_async(dispatch_get_main_queue(), ^{
  1236. self->_hud.label.text = [self->_theme localizedString:@"UGCKit.Record.ResourceLoadSucceeded"];
  1237. [self->_hud hideAnimated:YES afterDelay:1];
  1238. });
  1239. }
  1240. - (void)onLoadPituFailed
  1241. {
  1242. dispatch_async(dispatch_get_main_queue(), ^{
  1243. self->_hud.label.text = [self->_theme localizedString:@"UGCKit.Record.ResourceLoadFailed"];
  1244. [self->_hud hideAnimated:YES afterDelay:1];
  1245. });
  1246. }
  1247. #pragma mark TCBGMControllerListener
  1248. -(void) onBGMControllerPlay:(NSObject*) path{
  1249. [self dismissViewControllerAnimated:YES completion:nil];
  1250. if(path == nil) return;
  1251. [self onSetBGM:path];
  1252. //试听音乐这里要把RecordSpeed 设置为VIDEO_RECORD_SPEED_NOMAL,否则音乐可能会出现加速或则慢速播现象
  1253. [[TXUGCRecord shareInstance] setRecordSpeed:VIDEO_RECORD_SPEED_NOMAL];
  1254. [self playBGM:0];
  1255. dispatch_async(dispatch_get_main_queue(), ^(){
  1256. UGCKitVideoRecordMusicView *musicView = self->_musicView;
  1257. [musicView resetCutView];
  1258. if(musicView.hidden){
  1259. musicView.hidden = !musicView.hidden;
  1260. [self hideBottomView:!musicView.hidden];
  1261. }
  1262. });
  1263. }
  1264. #pragma mark - UGCKitAudioEffectPanelDelegate
  1265. - (void)audioEffectPanel:(UGCKitAudioEffectPanel *)panel didSelectReverbType:(TXVideoReverbType)type
  1266. {
  1267. _revertType = type;
  1268. [[TXUGCRecord shareInstance] setReverbType:type];
  1269. }
  1270. - (void)audioEffectPanel:(UGCKitAudioEffectPanel *)panel didSelectVoiceChangerType:(TXVideoVoiceChangerType)type
  1271. {
  1272. _voiceChangerType = type;
  1273. [[TXUGCRecord shareInstance] setVoiceChangerType:type];
  1274. }
  1275. -(void)onBtnMusicSelected
  1276. {
  1277. UINavigationController *nv = [[UINavigationController alloc] initWithRootViewController:_bgmListVC];
  1278. [nv.navigationBar setTitleTextAttributes:@{NSForegroundColorAttributeName:[UIColor whiteColor]}];
  1279. // nv.navigationBar.barTintColor = RGB(25, 29, 38);
  1280. nv.modalPresentationStyle = UIModalPresentationFullScreen;
  1281. [self presentViewController:nv animated:YES completion:nil];
  1282. [_bgmListVC loadBGMList];
  1283. }
  1284. -(void)onBtnMusicStoped
  1285. {
  1286. _BGMPath = nil;
  1287. _bgmRecording = NO;
  1288. [[TXUGCRecord shareInstance] stopBGM];
  1289. if (!_musicView.hidden) {
  1290. _musicView.hidden = !_musicView.hidden;
  1291. [self hideBottomView:!_musicView.hidden];
  1292. }
  1293. }
  1294. -(void)onBGMValueChange:(CGFloat)value
  1295. {
  1296. [[TXUGCRecord shareInstance] setBGMVolume:value];
  1297. }
  1298. -(void)onVoiceValueChange:(CGFloat)value
  1299. {
  1300. [[TXUGCRecord shareInstance] setMicVolume:value];
  1301. }
  1302. -(void)onBGMRangeChange:(CGFloat)startPercent endPercent:(CGFloat)endPercent
  1303. {
  1304. //切换bgm 范围的时候,bgm录制状态置NO
  1305. _bgmRecording = NO;
  1306. //试听音乐这里要把RecordSpeed 设置为VIDEO_RECORD_SPEED_NOMAL,否则音乐可能会出现加速或则慢速播现象
  1307. [[TXUGCRecord shareInstance] setRecordSpeed:VIDEO_RECORD_SPEED_NOMAL];
  1308. [self playBGM:_BGMDuration * startPercent toTime:_BGMDuration * endPercent];
  1309. }
  1310. #pragma mark - BGM Operations
  1311. -(void)onSetBGM:(NSObject *)path
  1312. {
  1313. _BGMPath = path;
  1314. if([_BGMPath isKindOfClass:[NSString class]]){
  1315. _BGMDuration = [[TXUGCRecord shareInstance] setBGM:(NSString *)_BGMPath];
  1316. }else{
  1317. _BGMDuration = [[TXUGCRecord shareInstance] setBGMAsset:(AVAsset *)_BGMPath];
  1318. }
  1319. _bgmRecording = NO;
  1320. dispatch_async(dispatch_get_main_queue(), ^{
  1321. [UGCKitProgressHUD hideHUDForView:self.view animated:YES];
  1322. });
  1323. }
  1324. -(void)playBGM:(CGFloat)beginTime{
  1325. if (_BGMPath != nil) {
  1326. [[TXUGCRecord shareInstance] playBGMFromTime:beginTime toTime:_BGMDuration withBeginNotify:^(NSInteger errCode) {
  1327. } withProgressNotify:^(NSInteger progressMS, NSInteger durationMS) {
  1328. } andCompleteNotify:^(NSInteger errCode) {
  1329. }];
  1330. _bgmBeginTime = beginTime;
  1331. }
  1332. }
  1333. -(void)playBGM:(CGFloat)beginTime toTime:(CGFloat)endTime
  1334. {
  1335. if (_BGMPath != nil) {
  1336. [[TXUGCRecord shareInstance] playBGMFromTime:beginTime toTime:endTime withBeginNotify:^(NSInteger errCode) {
  1337. } withProgressNotify:^(NSInteger progressMS, NSInteger durationMS) {
  1338. } andCompleteNotify:^(NSInteger errCode) {
  1339. }];
  1340. _bgmBeginTime = beginTime;
  1341. }
  1342. }
  1343. -(void)pauseBGM{
  1344. if (_BGMPath != nil) {
  1345. [[TXUGCRecord shareInstance] pauseBGM];
  1346. }
  1347. }
  1348. - (void)resumeBGM
  1349. {
  1350. if (_BGMPath != nil) {
  1351. [[TXUGCRecord shareInstance] resumeBGM];
  1352. }
  1353. }
  1354. #pragma mark - Misc Methods
  1355. - (void) toastTip:(NSString*)toastInfo
  1356. {
  1357. UGCKitProgressHUD *hud = [UGCKitProgressHUD showHUDAddedTo:self.view animated:YES];
  1358. hud.mode = UGCKitProgressHUDModeText;
  1359. hud.label.text = toastInfo;
  1360. dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC);
  1361. dispatch_after(popTime, dispatch_get_main_queue(), ^{
  1362. if ([hud.label.text isEqualToString:toastInfo]) {
  1363. [hud hideAnimated:YES];
  1364. }
  1365. });
  1366. }
  1367. -(void)alert:(NSString *)title msg:(NSString *)msg
  1368. {
  1369. UIAlertController *controller = [UIAlertController alertControllerWithTitle:title message:msg preferredStyle:UIAlertControllerStyleAlert];
  1370. [controller addAction:[UIAlertAction actionWithTitle:[_theme localizedString:@"UGCKit.Common.OK"] style:UIAlertActionStyleCancel handler:nil]];
  1371. [self presentViewController:controller animated:YES completion:nil];
  1372. }
  1373. #pragma mark - gesture handler
  1374. - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
  1375. CGPoint location = [gestureRecognizer locationInView:self.view];
  1376. CGRect frame = [self.view convertRect:_controlView.bottomMask.frame fromView:_controlView];
  1377. return !CGRectContainsPoint(frame, location);
  1378. }
  1379. - (void)onPanSlideFilter:(UIPanGestureRecognizer*)recognizer
  1380. {
  1381. CGPoint translation = [recognizer translationInView:self.view.superview];
  1382. [recognizer velocityInView:self.view];
  1383. CGPoint speed = [recognizer velocityInView:self.view];
  1384. // NSLog(@"pan center:(%.2f)", translation.x);
  1385. // NSLog(@"pan speed:(%.2f)", speed.x);
  1386. float ratio = translation.x / self.view.frame.size.width;
  1387. float leftRatio = ratio;
  1388. NSInteger index = [_vBeauty currentFilterIndex];
  1389. UIImage* curFilterImage = [_vBeauty filterImageByMenuOptionIndex:index];
  1390. UIImage* filterImage1 = nil;
  1391. UIImage* filterImage2 = nil;
  1392. CGFloat filter1Level = 0.f;
  1393. CGFloat filter2Level = 0.f;
  1394. if (leftRatio > 0) {
  1395. filterImage1 = [_vBeauty filterImageByMenuOptionIndex:index - 1];
  1396. filter1Level = [_vBeauty filterStrengthAtIndex:index - 1] / 10;
  1397. filterImage2 = curFilterImage;
  1398. filter2Level = [_vBeauty filterStrengthAtIndex:index] / 10;
  1399. }
  1400. else {
  1401. filterImage1 = curFilterImage;
  1402. filter1Level = [_vBeauty filterStrengthAtIndex:index] / 10;
  1403. filterImage2 = [_vBeauty filterImageByMenuOptionIndex:index + 1];
  1404. filter2Level = [_vBeauty filterStrengthAtIndex:index + 1] / 10;
  1405. leftRatio = 1 + leftRatio;
  1406. }
  1407. if (recognizer.state == UIGestureRecognizerStateChanged) {
  1408. [[TXUGCRecord shareInstance] setFilter:filterImage1
  1409. leftIntensity:filter1Level
  1410. rightFilter:filterImage2
  1411. rightIntensity:filter2Level
  1412. leftRatio:leftRatio];
  1413. }
  1414. else if (recognizer.state == UIGestureRecognizerStateEnded) {
  1415. BOOL isDependRadio = fabs(speed.x) < 500; //x方向的速度
  1416. [self animateFromFilter1:filterImage1
  1417. filter2:filterImage2
  1418. filter1Strength:filter1Level
  1419. filter2Strength:filter2Level
  1420. leftRadio:leftRatio
  1421. speed:speed.x
  1422. completion:^{
  1423. NSInteger filterIndex = index;
  1424. if (!isDependRadio) {
  1425. if (speed.x < 0) {
  1426. filterIndex = index + 1;
  1427. } else {
  1428. filterIndex = index - 1;
  1429. }
  1430. } else {
  1431. if (ratio > 0.5) { //过半或者速度>500就切换
  1432. filterIndex = index - 1;
  1433. } else if (ratio < -0.5) {
  1434. filterIndex = index + 1;
  1435. }
  1436. }
  1437. self->_vBeauty.currentFilterIndex = filterIndex;
  1438. UILabel* filterTipLabel = [UILabel new];
  1439. filterTipLabel.text = [self->_vBeauty currentFilterName];
  1440. filterTipLabel.font = [UIFont systemFontOfSize:30];
  1441. filterTipLabel.textColor = UIColor.whiteColor;
  1442. filterTipLabel.alpha = 0.1;
  1443. [filterTipLabel sizeToFit];
  1444. CGSize viewSize = self.view.frame.size;
  1445. CGFloat centerX = UGCKitRecordStyleDuet != self->_config.recordStyle ? viewSize.width / 2 : viewSize.width / 4;
  1446. CGFloat centerY = UGCKitRecordStyleTrio != self->_config.recordStyle ? viewSize.height / 3 : viewSize.height / 2;
  1447. filterTipLabel.center = CGPointMake(centerX, centerY);
  1448. [self.view addSubview:filterTipLabel];
  1449. [UIView animateWithDuration:0.25 animations:^{
  1450. filterTipLabel.alpha = 1;
  1451. } completion:^(BOOL finished) {
  1452. [UIView animateWithDuration:0.25 delay:0.25 options:UIViewAnimationOptionCurveLinear animations:^{
  1453. filterTipLabel.alpha = 0.1;
  1454. } completion:^(BOOL finished) {
  1455. [filterTipLabel removeFromSuperview];
  1456. }];
  1457. }];
  1458. }];
  1459. }
  1460. }
  1461. - (void)animateFromFilter1:(UIImage*)filter1Image
  1462. filter2:(UIImage*)filter2Image
  1463. filter1Strength:(CGFloat)filter1Strength
  1464. filter2Strength:(CGFloat)filter2Strength
  1465. leftRadio:(CGFloat)leftRadio
  1466. speed:(CGFloat)speed
  1467. completion:(void(^)(void))completion
  1468. {
  1469. if (leftRadio <= 0 || leftRadio >= 1) {
  1470. completion();
  1471. return;
  1472. }
  1473. static float delta = 1.f / 12;
  1474. BOOL isDependRadio = fabs(speed) < 500;
  1475. if (isDependRadio) {
  1476. if (leftRadio < 0.5) {
  1477. leftRadio -= delta;
  1478. }
  1479. else {
  1480. leftRadio += delta;
  1481. }
  1482. }
  1483. else {
  1484. if (speed > 0) {
  1485. leftRadio += delta;
  1486. }
  1487. else
  1488. leftRadio -= delta;
  1489. }
  1490. [[TXUGCRecord shareInstance] setFilter:filter1Image leftIntensity:filter1Strength rightFilter:filter2Image rightIntensity:filter2Strength leftRatio:leftRadio];
  1491. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.f / 30 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  1492. [self animateFromFilter1:filter1Image filter2:filter2Image filter1Strength:filter1Strength filter2Strength:filter2Strength leftRadio:leftRadio speed:speed completion:completion];
  1493. });
  1494. }
  1495. - (void)uinit{
  1496. [[TXUGCRecord shareInstance] stopRecord];
  1497. [[TXUGCRecord shareInstance] stopCameraPreview];
  1498. [[TXUGCRecord shareInstance].partsManager deleteAllParts];
  1499. _recordState = RecordStateStopped;
  1500. // [TCUtil removeCacheFile:_recordVideoPath];
  1501. // [TCUtil removeCacheFile:_joinVideoPath];
  1502. }
  1503. - (void)dealloc
  1504. {
  1505. [self uinit];
  1506. [[NSNotificationCenter defaultCenter] removeObserver:self];
  1507. }
  1508. @end
  1509. @implementation UGCKitRecordConfig
  1510. - (instancetype)init
  1511. {
  1512. self = [super init];
  1513. if (self) {
  1514. _ratio = VIDEO_ASPECT_RATIO_9_16;
  1515. _resolution = VIDEO_RESOLUTION_720_1280;
  1516. _videoBitrate = 9600;
  1517. _minDuration = 2;
  1518. _maxDuration = 16.0;
  1519. _audioSampleRate = AUDIO_SAMPLERATE_48000;
  1520. _fps = 30;
  1521. _gop = 3;
  1522. _AECEnabled = YES;
  1523. _autoComplete = YES;
  1524. }
  1525. return self;
  1526. }
  1527. @end
  1528. @implementation UGCKitRecordPreviewController
  1529. {
  1530. NSArray<NSString *> *_allVideoPaths;
  1531. NSString *_recordVideoPath;
  1532. UIView *_containerView;
  1533. UIView *_videoRecordView;
  1534. UGCKitRecordStyle _recordStyle;
  1535. NSArray<TXVideoEditer *> *_videoPlayers;
  1536. }
  1537. - (instancetype)initWithContainerView:(UIView *)containerView
  1538. recordStyle:(UGCKitRecordStyle)recordStyle
  1539. chorusVideos:(NSArray<NSString *> *)chorusVideos
  1540. recordVideo:(NSString *)recordVideo
  1541. {
  1542. if (!containerView) {
  1543. return nil;
  1544. }
  1545. if (self = [super init]) {
  1546. _containerView = containerView;
  1547. _recordStyle = recordStyle;
  1548. _recordVideoPath = recordVideo ? recordVideo : @"";
  1549. _videoRecordView = [[UIView alloc] initWithFrame:containerView.bounds];
  1550. _videoRecordView.autoresizingMask = UIViewAutoresizingFlexibleWidth
  1551. | UIViewAutoresizingFlexibleHeight;
  1552. [containerView addSubview:_videoRecordView];
  1553. [self initChildViews:chorusVideos];
  1554. [containerView bringSubviewToFront:_videoRecordView];
  1555. }
  1556. return self;
  1557. }
  1558. - (void)initChildViews:(NSArray<NSString *> *)chorusVideos
  1559. {
  1560. if (UGCKitRecordStyleRecord == _recordStyle) {
  1561. return;
  1562. }
  1563. NSMutableArray<TXVideoEditer *> *videoPlayers = [NSMutableArray arrayWithCapacity:chorusVideos.count];
  1564. void (^allocVideoPlayer)(UIView *, CGRect, NSString *, UIViewAutoresizing) = ^(UIView *containerView,
  1565. CGRect frame, NSString *videoPath,
  1566. UIViewAutoresizing autoresizingMask) {
  1567. UIView *playerView = [[UIView alloc] initWithFrame:frame];
  1568. playerView.autoresizingMask = autoresizingMask;
  1569. [containerView addSubview:playerView];
  1570. TXPreviewParam *param = [[TXPreviewParam alloc] init];
  1571. param.renderMode = PREVIEW_RENDER_MODE_FILL_SCREEN;
  1572. param.videoView = playerView;
  1573. TXVideoEditer *videoPlayer = [[TXVideoEditer alloc] initWithPreview:param];
  1574. [videoPlayer setVideoPath:videoPath];
  1575. [videoPlayers addObject:videoPlayer];
  1576. };
  1577. NSMutableArray<NSString *> *allVideoPaths = [NSMutableArray arrayWithCapacity:chorusVideos.count + 1];
  1578. [allVideoPaths addObject:_recordVideoPath];
  1579. if (UGCKitRecordStyleDuet == _recordStyle) { /// 分屏合拍模式
  1580. CGRect viewRect = CGRectMake(0, 0,
  1581. CGRectGetWidth(_containerView.frame) / 2,
  1582. CGRectGetHeight(_containerView.frame));
  1583. _videoRecordView.translatesAutoresizingMaskIntoConstraints = NO;
  1584. _videoRecordView.frame = viewRect;
  1585. _videoRecordView.autoresizingMask = UIViewAutoresizingFlexibleWidth
  1586. | UIViewAutoresizingFlexibleHeight
  1587. | UIViewAutoresizingFlexibleRightMargin;
  1588. UIViewAutoresizing autoresizingMask = UIViewAutoresizingFlexibleWidth
  1589. | UIViewAutoresizingFlexibleHeight
  1590. | UIViewAutoresizingFlexibleLeftMargin;
  1591. viewRect.origin.x = CGRectGetMaxX(viewRect);
  1592. NSString *videoPath = chorusVideos.firstObject;
  1593. allocVideoPlayer(_containerView, viewRect, videoPath, autoresizingMask);
  1594. [allVideoPaths addObject:(videoPath ? videoPath : @"")];
  1595. } else if (UGCKitRecordStyleTrio == _recordStyle) { /// 三屏合拍模式
  1596. CGRect viewRect = CGRectMake(0,
  1597. CGRectGetHeight(_containerView.frame) / 3,
  1598. CGRectGetWidth(_containerView.frame),
  1599. CGRectGetHeight(_containerView.frame) / 3);
  1600. _videoRecordView.translatesAutoresizingMaskIntoConstraints = NO;
  1601. _videoRecordView.frame = viewRect;
  1602. _videoRecordView.autoresizingMask = UIViewAutoresizingFlexibleWidth
  1603. | UIViewAutoresizingFlexibleHeight
  1604. | UIViewAutoresizingFlexibleTopMargin
  1605. | UIViewAutoresizingFlexibleBottomMargin;
  1606. for (NSUInteger idx = 0; idx < 2; idx++) {
  1607. NSString *videoPath = idx < chorusVideos.count ? chorusVideos[idx] : chorusVideos.firstObject;
  1608. if (0 == idx) {
  1609. CGRect playerRect = CGRectMake(0, CGRectGetMaxY(viewRect), viewRect.size.width, viewRect.size.height);
  1610. UIViewAutoresizing autoresizingMask = UIViewAutoresizingFlexibleWidth
  1611. | UIViewAutoresizingFlexibleHeight
  1612. | UIViewAutoresizingFlexibleBottomMargin;
  1613. allocVideoPlayer(_containerView, playerRect, videoPath, autoresizingMask);
  1614. [allVideoPaths insertObject:(videoPath ? videoPath : @"") atIndex:0];
  1615. } else {
  1616. CGRect playerRect = CGRectMake(0, 0, viewRect.size.width, viewRect.size.height);
  1617. UIViewAutoresizing autoresizingMask = UIViewAutoresizingFlexibleWidth
  1618. | UIViewAutoresizingFlexibleHeight
  1619. | UIViewAutoresizingFlexibleTopMargin;
  1620. allocVideoPlayer(_containerView, playerRect, videoPath, autoresizingMask);
  1621. [videoPlayers.lastObject setVideoVolume:0];
  1622. [allVideoPaths addObject:(videoPath ? videoPath : @"")];
  1623. }
  1624. }
  1625. }
  1626. _videoPlayers = videoPlayers;
  1627. _allVideoPaths = allVideoPaths;
  1628. }
  1629. - (instancetype)init
  1630. {
  1631. return nil;
  1632. }
  1633. - (NSArray<NSNumber *> *)allVideoVolumes
  1634. {
  1635. if (UGCKitRecordStyleRecord == _recordStyle) {
  1636. return @[@1];
  1637. } else if (UGCKitRecordStyleDuet == _recordStyle) {
  1638. return @[@0, @1];
  1639. } else {
  1640. return @[@1, @0, @0];
  1641. }
  1642. }
  1643. - (void)changeChorusVideo:(NSString *)videoPath
  1644. {
  1645. if (UGCKitRecordStyleRecord == _recordStyle || 0 == videoPath.length) {
  1646. return;
  1647. }
  1648. for (TXVideoEditer *videoPlayer in _videoPlayers) {
  1649. [videoPlayer stopPlay];
  1650. [videoPlayer setVideoPath:videoPath];
  1651. [videoPlayer previewAtTime:0];
  1652. }
  1653. if (UGCKitRecordStyleDuet == _recordStyle) {
  1654. _allVideoPaths = @[_recordVideoPath, videoPath];
  1655. } else if (UGCKitRecordStyleTrio == _recordStyle) {
  1656. _allVideoPaths = @[videoPath, _recordVideoPath, videoPath];
  1657. }
  1658. }
  1659. #pragma mark - start/stop play chorus videos
  1660. - (void)startPlayChorusVideos:(CGFloat)startTime toTime:(CGFloat)endTime
  1661. {
  1662. for (TXVideoEditer *videoPlayer in _videoPlayers) {
  1663. [videoPlayer startPlayFromTime:startTime toTime:endTime];
  1664. }
  1665. }
  1666. - (void)stopPlayChorusVideos
  1667. {
  1668. for (TXVideoEditer *videoPlayer in _videoPlayers) {
  1669. [videoPlayer stopPlay];
  1670. }
  1671. }
  1672. - (void)seekChorusVideosToTime:(CGFloat)time
  1673. {
  1674. for (TXVideoEditer *videoPlayer in _videoPlayers) {
  1675. [videoPlayer previewAtTime:time];
  1676. }
  1677. }
  1678. @end
  1679. @implementation UGCKitNavControllerPrivate
  1680. - (UIInterfaceOrientationMask)supportedInterfaceOrientations
  1681. {
  1682. return self.supportedOrientations;
  1683. }
  1684. - (UIStatusBarStyle)preferredStatusBarStyle
  1685. {
  1686. return UIStatusBarStyleLightContent;
  1687. }
  1688. @end