KSVideoRecordViewController.m 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691
  1. //
  2. // KSVideoRecordViewController.m
  3. // MusicGradeExam
  4. //
  5. // Created by Kyle on 2020/10/16.
  6. // Copyright © 2020 DayaMusic. All rights reserved.
  7. //
  8. #import "KSVideoRecordViewController.h"
  9. #import "AppDelegate.h"
  10. #import "UIDevice+TFDevice.h"
  11. #import <AVFoundation/AVFoundation.h>
  12. #import <AssetsLibrary/AssetsLibrary.h>
  13. #import "KSNormalAlertView.h"
  14. #import "SubjectImageModel.h"
  15. #import "TZImageManager.h"
  16. @interface KSVideoRecordViewController ()<AVCaptureFileOutputRecordingDelegate>
  17. @property (nonatomic, strong) AVAudioPlayer *audioPlayer;
  18. //会话 负责输入和输出设备之间的数据传递
  19. @property (nonatomic, strong) AVCaptureSession *captureSession;
  20. @property (nonatomic, strong) AVCaptureDeviceInput *videoCaptureDeviceInput;
  21. @property (nonatomic, strong) AVCaptureDeviceInput *audioCaptureDeviceInput;
  22. // 视频流输出
  23. @property (nonatomic, strong) AVCaptureMovieFileOutput *captureMovieFileOutput;
  24. // 相机拍摄预览图层
  25. @property (nonatomic, strong) AVCaptureVideoPreviewLayer *captureVideoPreviewLayer;
  26. // 自定义UI控件容器
  27. @property (nonatomic, strong) UIView *viewContainer;
  28. // 录制时常
  29. @property (nonatomic, strong) UILabel *timeLabel;
  30. // 计时器
  31. @property (nonatomic, strong) NSTimer *timer;
  32. @property (nonatomic, strong) UIButton *bottomButton;
  33. @property (nonatomic, strong) SubjectImageModel *guideImageModel;
  34. @property (nonatomic, strong) UIImageView *tipsImage;
  35. @property (nonatomic, assign) NSInteger prepareTime;
  36. @property (nonatomic, assign) BOOL isPrepare;
  37. @property (nonatomic, strong) UILabel *bottomCountLabel;
  38. @property (nonatomic, assign) NSInteger currentRecordIndex;
  39. @property (nonatomic, strong) UILabel *topTipsLabel;
  40. @property (nonatomic, strong) UIView *topTipsView;
  41. @property (nonatomic, strong) UIImageView *warningImage;
  42. @property (nonatomic, assign) NSInteger duration;
  43. @property (nonatomic, copy) VideoRecordBlock callback;
  44. @property (nonatomic, strong) NSMutableArray *videoAssetArray;
  45. @property (nonatomic, assign) BOOL isStartExam;
  46. @property (nonatomic, assign) BOOL isRecording;
  47. @property (nonatomic, assign) BOOL isQuiting;
  48. @property (nonatomic, strong) UIView *songCardView;
  49. @property (nonatomic, strong) UILabel *subjectLabel;
  50. @property (nonatomic, strong) UILabel *songTitle;
  51. @property (nonatomic, strong) UILabel *songNameLabel;
  52. @property (nonatomic, strong) UILabel *songTimeLabel;
  53. @property (nonatomic, assign) NSInteger songCountTime;
  54. @end
  55. @implementation KSVideoRecordViewController
  56. - (void)viewWillAppear:(BOOL)animated {
  57. [super viewWillAppear:animated];
  58. self.zh_statusBarHidden = YES;
  59. // 切换到横屏
  60. AppDelegate* delegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
  61. delegate.allowAutoRotate = YES;
  62. [UIDevice switchNewOrientation:UIInterfaceOrientationLandscapeRight];
  63. [[UIApplication sharedApplication] setIdleTimerDisabled:YES];
  64. }
  65. - (void)viewWillDisappear:(BOOL)animated {
  66. [super viewWillDisappear:animated];
  67. // 竖屏
  68. AppDelegate* delegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
  69. delegate.allowAutoRotate = NO;
  70. [UIDevice switchNewOrientation:UIInterfaceOrientationPortrait];
  71. [[UIApplication sharedApplication] setIdleTimerDisabled:NO];
  72. self.zh_statusBarHidden = NO;
  73. }
  74. - (void)viewDidLoad {
  75. [super viewDidLoad];
  76. // Do any additional setup after loading the view.
  77. self.view.backgroundColor = HexRGB(0x141414);
  78. [self configMessage];
  79. // 切换到横屏
  80. AppDelegate* delegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
  81. delegate.allowAutoRotate = YES;
  82. [UIDevice switchNewOrientation:UIInterfaceOrientationLandscapeRight];
  83. [self configUI];
  84. [self.timer setFireDate:[NSDate distantPast]];
  85. }
  86. - (void)configMessage {
  87. self.prepareTime = 10;
  88. self.currentRecordIndex = 0;
  89. }
  90. - (void)configUI {
  91. _captureSession = [[AVCaptureSession alloc] init];
  92. // 初始化会话对象
  93. if ([_captureSession canSetSessionPreset:AVCaptureSessionPresetInputPriority]) {
  94. _captureSession.sessionPreset = AVCaptureSessionPresetInputPriority;
  95. }
  96. NSError *error = nil;
  97. // 获取视频输出对象
  98. AVCaptureDevice *videoCaptureDevice = [self cameraDeviceWithPosition:(AVCaptureDevicePositionBack)];
  99. if (!videoCaptureDevice) {
  100. NSLog(@"获取后置摄像头失败!");
  101. return;
  102. }
  103. _videoCaptureDeviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:videoCaptureDevice error:&error];
  104. if (error) {
  105. NSLog(@"取得视频设备输入对象错误");
  106. return;
  107. }
  108. // 获取音频输入对象
  109. AVCaptureDevice *audioCaptureDevice = [[AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio] firstObject];
  110. _audioCaptureDeviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:audioCaptureDevice error:&error];
  111. if (error) {
  112. NSLog(@"取得音频设备输入对象时出错");
  113. return;
  114. }
  115. // 初始化设备输出对象
  116. _captureMovieFileOutput = [[AVCaptureMovieFileOutput alloc] init];
  117. //将设备输入添加到会话中
  118. if ([_captureSession canAddInput:_videoCaptureDeviceInput]) {
  119. [_captureSession addInput:_videoCaptureDeviceInput];
  120. [_captureSession addInput:_audioCaptureDeviceInput];
  121. //防抖功能
  122. AVCaptureConnection *captureConnection = [_captureMovieFileOutput connectionWithMediaType:AVMediaTypeAudio];
  123. if ([captureConnection isVideoStabilizationSupported]) {
  124. captureConnection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeAuto;
  125. }
  126. }
  127. //将设备输出添加到会话中
  128. if ([_captureSession canAddOutput:_captureMovieFileOutput]) {
  129. [_captureSession addOutput:_captureMovieFileOutput];
  130. }
  131. //创建视频预览图层
  132. _captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_captureSession];
  133. self.viewContainer.layer.masksToBounds = YES;
  134. _captureVideoPreviewLayer.frame = self.viewContainer.bounds;
  135. _captureVideoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
  136. _captureVideoPreviewLayer.connection.videoOrientation = AVCaptureVideoOrientationLandscapeRight;
  137. [self.view.layer addSublayer:_captureVideoPreviewLayer];
  138. [self.view addSubview:self.viewContainer];
  139. [self configSongCardView];
  140. self.songCardView.hidden = YES;
  141. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  142. [self playTipsWithFileName:@"tips_one"];
  143. });
  144. }
  145. - (void)playTipsWithFileName:(NSString *)fileName {
  146. // 播放提示音
  147. NSString *path = [[NSBundle mainBundle] pathForResource:fileName ofType:@"caf"];
  148. if (path) {
  149. NSURL *pathUrl = [NSURL fileURLWithPath:path];
  150. NSError *error = nil;
  151. self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:pathUrl error:&error];
  152. self.audioPlayer.volume = 1.0f;
  153. [self.audioPlayer prepareToPlay];
  154. if (error == nil) {
  155. [self.audioPlayer play];
  156. }
  157. }
  158. }
  159. - (void)configSongCardView {
  160. [self.view addSubview:self.songCardView];
  161. [self.songCardView mas_makeConstraints:^(MASConstraintMaker *make) {
  162. make.width.mas_equalTo(180);
  163. make.right.mas_equalTo(self.view.mas_right);
  164. make.centerY.mas_equalTo(self.view.mas_centerY);
  165. }];
  166. self.songCardView.layer.cornerRadius = 5.0f;
  167. [self.songCardView addSubview:self.subjectLabel];
  168. self.subjectLabel.text = [NSString stringWithFormat:@"%@,%@", self.subjectName, self.leverName];
  169. [self.subjectLabel mas_makeConstraints:^(MASConstraintMaker *make) {
  170. make.left.mas_equalTo(self.songCardView.mas_left).offset(10);
  171. make.right.mas_equalTo(self.songCardView.mas_right).offset(10);
  172. make.top.mas_equalTo(self.songCardView.mas_top).offset(10);
  173. make.height.mas_equalTo(20);
  174. }];
  175. [self.songCardView addSubview:self.songTitle];
  176. [self.songTitle mas_makeConstraints:^(MASConstraintMaker *make) {
  177. make.left.mas_equalTo(self.songCardView.mas_left).offset(10);
  178. make.right.mas_equalTo(self.songCardView.mas_right).offset(10);
  179. make.top.mas_equalTo(self.subjectLabel.mas_bottom).offset(10);
  180. make.height.mas_equalTo(20);
  181. }];
  182. [self.songCardView addSubview:self.songNameLabel];
  183. self.songNameLabel.text = [self.songMessageArray firstObject];
  184. [self.songNameLabel mas_makeConstraints:^(MASConstraintMaker *make) {
  185. make.left.mas_equalTo(self.songCardView.mas_left).offset(10);
  186. make.right.mas_equalTo(self.songCardView.mas_right).offset(10);
  187. make.top.mas_equalTo(self.songTitle.mas_bottom).offset(10);
  188. }];
  189. [self.songCardView addSubview:self.songTimeLabel];
  190. [self.songTimeLabel mas_makeConstraints:^(MASConstraintMaker *make) {
  191. make.left.mas_equalTo(self.songCardView.mas_left).offset(10);
  192. make.right.mas_equalTo(self.songCardView.mas_right).offset(10);
  193. make.top.mas_equalTo(self.songNameLabel.mas_bottom).offset(10);
  194. make.bottom.mas_equalTo(self.songCardView.mas_bottom).offset(-10);
  195. }];
  196. self.songCountTime = self.singleSongRecordMinutes * 60;
  197. self.songTimeLabel.text = [NSString stringWithFormat:@"当前曲目剩余时间:%zds",self.songCountTime];
  198. }
  199. -(void)viewDidAppear:(BOOL)animated {
  200. [super viewDidAppear:animated];
  201. [self.captureSession startRunning];
  202. }
  203. -(void)viewDidDisappear:(BOOL)animated {
  204. [super viewDidDisappear:animated];
  205. [self.captureSession stopRunning];
  206. [self stopDurationTimer];
  207. }
  208. #pragma mark ---- buttonAction
  209. - (void)bottomButtonAction:(UIButton *)sender {
  210. if ([sender.titleLabel.text isEqualToString:@"已就位"]) {
  211. [self playTipsWithFileName:@"tips_second"];
  212. [sender setTitle:@"开始考级" forState:UIControlStateNormal];
  213. }
  214. else if ([sender.titleLabel.text isEqualToString:@"开始考级"]) {
  215. self.isPrepare = YES;
  216. self.topTipsLabel.text = @"请考生在10秒内就位演奏";
  217. [self playTipsWithFileName:@"tips_third"];
  218. self.topTipsView.hidden = NO;
  219. self.bottomCountLabel.hidden = NO;
  220. if (self.currentRecordIndex + 1 == self.songMessageArray.count) {
  221. [sender setTitle:@"完成录制" forState:UIControlStateNormal];
  222. }
  223. else {
  224. [sender setTitle:@"下一首" forState:UIControlStateNormal];
  225. }
  226. sender.hidden = YES;
  227. }
  228. else if ([sender.titleLabel.text isEqualToString:@"下一首"]) {
  229. [KSNormalAlertView ks_showAlertWithTitle:@"确定切换下一首吗?" leftTitle:@"取消" rightTitle:@"确定" cancel:^{
  230. } confirm:^{
  231. [self nextSongAction];
  232. }];
  233. }
  234. else if ([sender.titleLabel.text isEqualToString:@"完成录制"]) {
  235. [KSNormalAlertView ks_showAlertWithTitle:@"确定完成录制吗?" leftTitle:@"取消" rightTitle:@"确定" cancel:^{
  236. } confirm:^{
  237. // 退出录播 返回数据
  238. if (self.isRecording) {
  239. if ([self.captureMovieFileOutput isRecording]) {
  240. [self.captureMovieFileOutput stopRecording];
  241. }
  242. self.isQuiting = YES;
  243. [MBProgressHUD ksShowHUDWithText:@"视频保存中..."];
  244. }
  245. else {
  246. self.callback(VIDEORECORDACTION_BACK, self.videoAssetArray);
  247. [self quitRecordExam];
  248. }
  249. }];
  250. }
  251. }
  252. - (void)nextSongAction {
  253. self.currentRecordIndex++;
  254. self.topTipsLabel.text = @"请考生在10秒内就位演奏";
  255. self.prepareTime = 10;
  256. self.isPrepare = YES;
  257. [self.captureMovieFileOutput stopRecording];
  258. if (self.currentRecordIndex + 1 == self.songMessageArray.count) {
  259. [self.bottomButton setTitle:@"完成录制" forState:UIControlStateNormal];
  260. }
  261. else {
  262. [self.bottomButton setTitle:@"下一首" forState:UIControlStateNormal];
  263. }
  264. self.bottomButton.hidden = YES;
  265. }
  266. - (void)setCurrentRecordIndex:(NSInteger)currentRecordIndex {
  267. _currentRecordIndex = currentRecordIndex;
  268. self.songCountTime = self.singleSongRecordMinutes * 60;
  269. self.songTimeLabel.text = [NSString stringWithFormat:@"当前曲目剩余时间:%zds",self.songCountTime];
  270. if (self.songMessageArray.count > currentRecordIndex) {
  271. NSString *songName = self.songMessageArray[currentRecordIndex];
  272. self.songNameLabel.text = songName;
  273. }
  274. }
  275. #pragma mark -------- AVCaptureFileOutputRecordingDelegate ----------
  276. - (void)captureOutput:(AVCaptureFileOutput *)captureOutput didStartRecordingToOutputFileAtURL:(NSURL *)fileURL fromConnections:(NSArray *)connections {
  277. NSLog(@"开始录制");
  278. _isRecording = YES;
  279. }
  280. - (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error {
  281. NSLog(@"录制结束");
  282. // 是否保存到相册
  283. [[TZImageManager manager] saveVideoWithUrl:outputFileURL completion:^(PHAsset *asset, NSError *error) {
  284. self.isRecording = NO;
  285. // 删除文件
  286. [self removeVideoWithPath:outputFileURL.path];
  287. // 记录当前相册asset
  288. [self.videoAssetArray addObject:asset];
  289. if (self.isQuiting) { // 保存完成退出
  290. [MBProgressHUD ksHideHUD];
  291. self.callback(VIDEORECORDACTION_BACK, self.videoAssetArray);
  292. [self quitRecordExam];
  293. }
  294. }];
  295. }
  296. - (void)removeVideoWithPath:(NSString *)videoUrl {
  297. NSFileManager *fileMamager = [NSFileManager defaultManager];
  298. if ([fileMamager fileExistsAtPath:videoUrl]) {
  299. [fileMamager removeItemAtPath:videoUrl error:nil];
  300. }
  301. }
  302. /**取得指定位置的摄像头*/
  303. - (AVCaptureDevice *)cameraDeviceWithPosition:(AVCaptureDevicePosition)position {
  304. NSArray *cameras = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
  305. for (AVCaptureDevice *camera in cameras) {
  306. if ([camera position] == position) {
  307. return camera;
  308. }
  309. }
  310. return nil;
  311. }
  312. #pragma mark ---- lazying
  313. // 乐曲卡片
  314. - (UIView *)songCardView {
  315. if (!_songCardView) {
  316. _songCardView = [[UIView alloc] init];
  317. _songCardView.backgroundColor = HexRGBAlpha(0x141414, 0.5f);
  318. }
  319. return _songCardView;
  320. }
  321. - (UILabel *)subjectLabel {
  322. if (!_subjectLabel) {
  323. _subjectLabel = [[UILabel alloc] init];
  324. _subjectLabel.textColor = HexRGB(0xffffff);
  325. _subjectLabel.font = [UIFont systemFontOfSize:14.0f];
  326. }
  327. return _subjectLabel;
  328. }
  329. - (UILabel *)songTitle {
  330. if (!_songTitle) {
  331. _songTitle = [[UILabel alloc] init];
  332. _songTitle.textColor = THEMECOLOR;
  333. _songTitle.font = [UIFont systemFontOfSize:14.0f];
  334. _songTitle.text = @"当前曲目:";
  335. }
  336. return _songTitle;
  337. }
  338. - (UILabel *)songNameLabel {
  339. if (!_songNameLabel) {
  340. _songNameLabel = [[UILabel alloc] init];
  341. _songNameLabel.textColor = HexRGB(0xffffff);
  342. _songNameLabel.numberOfLines = 0;
  343. _songNameLabel.font = [UIFont systemFontOfSize:14.0f];
  344. }
  345. return _songNameLabel;
  346. }
  347. - (UILabel *)songTimeLabel {
  348. if (!_songTimeLabel) {
  349. _songTimeLabel = [[UILabel alloc] init];
  350. _songTimeLabel.textColor = HexRGB(0xffffff);
  351. _songTimeLabel.font = [UIFont systemFontOfSize:14.0f];
  352. }
  353. return _songTimeLabel;
  354. }
  355. - (NSMutableArray *)videoAssetArray {
  356. if (!_videoAssetArray) {
  357. _videoAssetArray = [NSMutableArray arrayWithCapacity:self.songMessageArray.count];
  358. }
  359. return _videoAssetArray;
  360. }
  361. - (UILabel *)timeLabel {
  362. if (!_timeLabel) {
  363. _timeLabel = [[UILabel alloc] init];
  364. _timeLabel.font = [UIFont systemFontOfSize:14.0f];
  365. _timeLabel.textColor = HexRGB(0xffffff);
  366. }
  367. return _timeLabel;
  368. }
  369. - (void)configTime:(NSInteger)subTime recordFinishBlock:(VideoRecordBlock)block {
  370. if (block) {
  371. self.callback = block;
  372. }
  373. self.duration = subTime;
  374. }
  375. - (void)setDuration:(NSInteger)duration {
  376. _duration = duration;
  377. if (_duration < 300) {
  378. // self.timeLabel.textColor = HexRGB(0xff3535);
  379. }
  380. if (_duration < 0) {
  381. [self stopDurationTimer];
  382. if (self.callback) {
  383. self.callback(VIDEORECORDACTION_OVER,self.videoAssetArray);
  384. }
  385. }
  386. }
  387. - (void)stopDurationTimer {
  388. [self.timer setFireDate:[NSDate distantFuture]];
  389. if (self.timer.valid) {
  390. [self.timer invalidate];
  391. self.timer = nil;
  392. }
  393. }
  394. - (NSString *)formatTime {
  395. if (self.duration < 0) {
  396. return @"00分00秒";
  397. }
  398. NSInteger durationInteger = self.duration--;
  399. NSInteger durationS = durationInteger % 60;
  400. NSInteger durationM = (durationInteger - durationS) / 60;
  401. NSString *minuteStr = [NSString stringWithFormat:@"%02ld", durationM];
  402. NSString *secondStr = [NSString stringWithFormat:@"%02ld", durationS];
  403. return [NSString stringWithFormat:@"%@分%@秒", minuteStr, secondStr];
  404. }
  405. - (UILabel *)topTipsLabel {
  406. if (!_topTipsLabel) {
  407. _topTipsLabel = [[UILabel alloc] init];
  408. _topTipsLabel.font = [UIFont systemFontOfSize:14.0f];
  409. _topTipsLabel.textColor = HexRGB(0x00ffef);
  410. }
  411. return _topTipsLabel;
  412. }
  413. - (NSTimer *)timer {
  414. if (_timer == nil) {
  415. _timer = [NSTimer scheduledTimerWithTimeInterval:1
  416. target:self
  417. selector:@selector(timeFunction:)
  418. userInfo:nil
  419. repeats:YES];
  420. [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
  421. }
  422. return _timer;
  423. }
  424. - (void)timeFunction:(NSTimer *)timer {
  425. if (self.isPrepare) {
  426. if (self.prepareTime > 0) {
  427. self.bottomCountLabel.text = [NSString stringWithFormat:@"%zds",self.prepareTime];
  428. self.bottomCountLabel.hidden = NO;
  429. self.prepareTime--;
  430. }
  431. else {
  432. self.isStartExam = YES;
  433. self.isPrepare = NO;
  434. self.topTipsLabel.text = @"正在考级,学生必须保持在画面之内";
  435. self.songCardView.hidden = NO;
  436. self.bottomCountLabel.hidden = YES;
  437. self.bottomButton.hidden = NO;
  438. NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject] stringByAppendingPathComponent:@"VideoData"];
  439. NSFileManager *fileManager = [NSFileManager defaultManager];
  440. BOOL isDir = FALSE;
  441. BOOL isDirExist = [fileManager fileExistsAtPath:path isDirectory:&isDir];
  442. if(!(isDirExist && isDir)) {
  443. BOOL bCreateDir = [fileManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];
  444. if(!bCreateDir){
  445. NSLog(@"创建文件夹失败!");
  446. }
  447. NSLog(@"创建文件夹成功,文件路径%@",path);
  448. }
  449. NSString *songName = [[self.songMessageArray objectAtIndex:self.currentRecordIndex] MD5];
  450. NSString *fileName = [NSString stringWithFormat:@"%@.mp4",songName];
  451. NSString *filePath = [path stringByAppendingPathComponent:fileName];
  452. NSLog(@"%@",filePath);
  453. [self.captureMovieFileOutput startRecordingToOutputFileURL:[NSURL fileURLWithPath:filePath] recordingDelegate:self];
  454. }
  455. }
  456. else {
  457. if (_isStartExam) {
  458. self.songCountTime--;
  459. if (self.songCountTime >= 0) {
  460. self.songTimeLabel.text = [NSString stringWithFormat:@"当前曲目剩余时间:%zds",self.songCountTime];
  461. }
  462. if (self.songCountTime == 0) {
  463. _isStartExam = NO;
  464. // 切换下一首
  465. [self nextSongAction];
  466. }
  467. }
  468. }
  469. self.timeLabel.text = [NSString stringWithFormat:@"考级剩余:%@",[self formatTime]];
  470. }
  471. - (UIImageView *)tipsImage {
  472. if (!_tipsImage) {
  473. _tipsImage = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, kScreenHeight)];
  474. }
  475. return _tipsImage;
  476. }
  477. - (SubjectImageModel *)guideImageModel {
  478. if (!_guideImageModel) {
  479. _guideImageModel = [[SubjectImageModel alloc] init];
  480. _guideImageModel.subjectName = self.subjectName;
  481. }
  482. return _guideImageModel;
  483. }
  484. - (UIButton *)bottomButton {
  485. if (!_bottomButton) {
  486. _bottomButton = [UIButton buttonWithType:UIButtonTypeCustom];
  487. [_bottomButton setTitle:@"已就位" forState:UIControlStateNormal];
  488. _bottomButton.frame = CGRectMake(kScreenWidth - 120 - 20, 10, 120, 44);
  489. _bottomButton.layer.cornerRadius = 22;
  490. _bottomButton.titleLabel.font = [UIFont systemFontOfSize:14.0f];
  491. _bottomButton.backgroundColor = HexRGB(0xff5a5a);
  492. [_bottomButton addTarget:self action:@selector(bottomButtonAction:) forControlEvents:UIControlEventTouchUpInside];
  493. }
  494. return _bottomButton;
  495. }
  496. - (UIView *)topTipsView {
  497. if (!_topTipsView) {
  498. _topTipsView = [[UIView alloc] init];
  499. _warningImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"warning_image"]];
  500. _warningImage.frame = CGRectMake(0, 4, 14, 12);
  501. [_topTipsView addSubview:_warningImage];
  502. [_topTipsView addSubview:self.topTipsLabel];
  503. }
  504. return _topTipsView;
  505. }
  506. - (UIView *)viewContainer {
  507. if (!_viewContainer) {
  508. _viewContainer = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, kScreenHeight)];
  509. // 声部示意图
  510. NSString *tipsImageName = self.guideImageModel.guideImage;
  511. if (![NSString isEmptyString:tipsImageName]) {
  512. [self.tipsImage setImage:[UIImage imageNamed:tipsImageName]];
  513. [self.viewContainer addSubview:self.tipsImage];
  514. }
  515. UIView *headView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, 64)];
  516. headView.backgroundColor = HexRGBAlpha(0x141414, 0.5f);
  517. [_viewContainer addSubview:headView];
  518. UIButton *cancleButton = [UIButton buttonWithType:UIButtonTypeCustom];
  519. cancleButton.frame = CGRectMake(kScreenWidth - 120 - iPhoneXSafeBottomMargin, 0, 120, 64);
  520. cancleButton.titleLabel.font = [UIFont systemFontOfSize:14.0f];
  521. [cancleButton addTarget:self action:@selector(cancleRecordAction) forControlEvents:UIControlEventTouchUpInside];
  522. [cancleButton setTitle:@"放弃本次录制" forState:UIControlStateNormal];
  523. [headView addSubview:cancleButton];
  524. [headView addSubview:self.topTipsView];
  525. [self.topTipsView mas_makeConstraints:^(MASConstraintMaker *make) {
  526. make.centerX.mas_equalTo(headView.mas_centerX);
  527. make.centerY.mas_equalTo(headView.mas_centerY);
  528. make.height.mas_equalTo(20);
  529. }];
  530. [self.topTipsLabel mas_makeConstraints:^(MASConstraintMaker *make) {
  531. make.top.bottom.right.mas_equalTo(self.topTipsView);
  532. make.left.mas_equalTo(self.warningImage.mas_right).offset(5);
  533. }];
  534. self.topTipsView.hidden = YES;
  535. [headView addSubview:self.timeLabel];
  536. [self.timeLabel mas_makeConstraints:^(MASConstraintMaker *make) {
  537. make.left.mas_equalTo(headView.mas_left).offset(15);
  538. make.centerY.mas_equalTo(headView.mas_centerY);
  539. make.height.mas_equalTo(20);
  540. }];
  541. UIView *bottomView = [[UIView alloc] initWithFrame:CGRectMake(0, kScreenHeight - 64, kScreenWidth, 64)];
  542. bottomView.backgroundColor = HexRGBAlpha(0x141414, 0.5f);
  543. [_viewContainer addSubview:bottomView];
  544. [bottomView addSubview:self.bottomButton];
  545. [bottomView addSubview:self.bottomCountLabel];
  546. [self.bottomCountLabel mas_makeConstraints:^(MASConstraintMaker *make) {
  547. make.height.mas_equalTo(20);
  548. make.center.mas_equalTo(bottomView);
  549. make.width.mas_equalTo(40);
  550. }];
  551. self.bottomCountLabel.hidden = YES;
  552. }
  553. return _viewContainer;
  554. }
  555. - (UILabel *)bottomCountLabel {
  556. if (!_bottomCountLabel) {
  557. _bottomCountLabel = [[UILabel alloc] init];
  558. _bottomCountLabel.textColor = [UIColor whiteColor];
  559. _bottomCountLabel.font = [UIFont systemFontOfSize:16.0f];
  560. }
  561. return _bottomCountLabel;
  562. }
  563. - (void)cancleRecordAction {
  564. // 弹窗提示退出房间
  565. [KSNormalAlertView ks_showAlertWithTitle:@"确定退出录播吗?" leftTitle:@"取消" rightTitle:@"确定" cancel:^{
  566. } confirm:^{
  567. // 退出录播
  568. [self quitRecordExam];
  569. }];
  570. }
  571. - (void)quitRecordExam {
  572. [self dismissViewControllerAnimated:NO completion:^{
  573. if ([self.captureMovieFileOutput isRecording]) {
  574. [self.captureMovieFileOutput stopRecording];
  575. }
  576. }];
  577. }
  578. /*
  579. #pragma mark - Navigation
  580. // In a storyboard-based application, you will often want to do a little preparation before navigation
  581. - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
  582. // Get the new view controller using [segue destinationViewController].
  583. // Pass the selected object to the new view controller.
  584. }
  585. */
  586. @end