1/* 2* Copyright (C) 2023 Huawei Device Co., Ltd. 3* Licensed under the Apache License, Version 2.0 (the "License"); 4* you may not use this file except in compliance with the License. 5* You may obtain a copy of the License at 6* 7* http://www.apache.org/licenses/LICENSE-2.0 8* 9* Unless required by applicable law or agreed to in writing, software 10* distributed under the License is distributed on an "AS IS" BASIS, 11* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12* See the License for the specific language governing permissions and 13* limitations under the License. 14*/ 15import audio from '@ohos.multimedia.audio'; 16import fs from '@ohos.file.fs'; 17import common from '@ohos.app.ability.common'; 18import router from '@ohos.router'; 19import resourceManager from '@ohos.resourceManager'; 20import { BusinessError } from '@ohos.base'; 21 22const TOTAL_SECOND = 30; 23const NORMAL_INDEX = 0; 24const SCREEN_INDEX = 1; 25const MIN_RECORD_SECOND = 5; 26const RANDOM_NUM = 10000; 27const INTERVAL_TIME = 1000; 28const READ_TIME_OUT_SCREEN = 0; 29const READ_TIME_OUT_NORMAL = 0; 30class Options { 31 offset: number = 0; 32 length: number = 0; 33} 34 35@Entry 36@Component 37struct ParallelCapturer { 38 @State fontColor: string = '#182431'; 39 @State selectedFontColor: string = '#007DFF'; 40 @State currentIndex: number = 1; 41 // Capturer 42 private audioCapturerNormal?: audio.AudioCapturer; 43 private audioCapturerScreen?: audio.AudioCapturer; 44 @State recordState: string = 'init'; // [init,started,stoped] 45 @State recordSec: number = 0; 46 private interval: number = 0; 47 @State showTime: string = '00:00:00'; 48 private audioCapturerOptionNormal: audio.AudioCapturerOptions = { 49 streamInfo: { 50 samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_44100, 51 channels: audio.AudioChannel.CHANNEL_2, 52 sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, 53 encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW 54 }, 55 capturerInfo: { 56 source: audio.SourceType.SOURCE_TYPE_MIC, 57 capturerFlags: 0 58 } 59 }; 60 private audioCapturerOptionScreen: audio.AudioCapturerOptions = { 61 streamInfo: { 62 samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_44100, 63 channels: audio.AudioChannel.CHANNEL_2, 64 sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, 65 encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW 66 }, 67 capturerInfo: { 68 source: 2, 69 capturerFlags: 0 70 }, 71 playbackCaptureConfig: { 72 filterOptions: { 73 usages: [audio.StreamUsage.STREAM_USAGE_MEDIA] 74 } 75 } 76 }; 77 // recorder data 78 private audioRendererOptions: audio.AudioRendererOptions = { 79 streamInfo: { 80 samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_44100, 81 channels: audio.AudioChannel.CHANNEL_2, 82 sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, 83 encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW 84 }, 85 rendererInfo: { 86 content: audio.ContentType.CONTENT_TYPE_MUSIC, 87 usage: audio.StreamUsage.STREAM_USAGE_MEDIA, 88 rendererFlags: 0 89 } 90 }; 91 private bufferSizeNormal = 0; 92 private bufferSizeScreen = 0; 93 @State date: string = ''; 94 private audioRenderers: audio.AudioRenderer[] = []; 95 @State titleList: string[] = ['', '']; 96 @State pathList: string[] = ['', '']; 97 @State fdList: number[] = [0, 0]; 98 @State playSecList: number[] = [0, 0]; 99 @State renderStateList: number[] = [0, 0]; 100 @State renderStartOffsetList: number[] = [0, 0]; 101 @State isRecordOver: boolean = false; 102 103 // Music Player 104 private audioRendererOptionsMusic: audio.AudioRendererOptions = { 105 streamInfo: { 106 samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_44100, 107 channels: audio.AudioChannel.CHANNEL_2, 108 sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, 109 encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW 110 }, 111 rendererInfo: { 112 content: audio.ContentType.CONTENT_TYPE_MUSIC, 113 usage: audio.StreamUsage.STREAM_USAGE_MEDIA, 114 rendererFlags: 0 115 }, 116 privacyType: 0 117 }; 118 private audioRendererMusic?: audio.AudioRenderer; 119 @State renderMusicState: number = 0; 120 @State curTimeSec: number = 0; 121 @State startMusicOffset: number = 0; 122 private appContext?: common.Context; 123 private fileDescriptor?: resourceManager.RawFileDescriptor; 124 private audioSource = 'test1.wav'; 125 126 @Builder TabBuilder(index: number, btnId: string) { 127 Column() { 128 Text(index === 0 ? $r('app.string.NORMAL_CAPTURER') : $r('app.string.PARALLEL_CAPTURER')) 129 .fontColor(this.currentIndex === index ? this.selectedFontColor : this.fontColor) 130 .opacity(this.currentIndex === index ? 1 : 0.6) 131 .fontSize(16) 132 .fontWeight(this.currentIndex === index ? 500 : 400) 133 .lineHeight(22) 134 .margin({ top: 17, bottom: 7 }); 135 Divider() 136 .strokeWidth(2) 137 .color('#007DFF') 138 .opacity(this.currentIndex === index ? 1 : 0); 139 }.width(78).id('btn_' + btnId); 140 } 141 142 async aboutToAppear(): Promise<void> { 143 console.log('ParallelCapturer aboutToAppear'); 144 await this.initResource(); 145 } 146 147 async initResource(): Promise<void> { 148 this.appContext = getContext(this); 149 this.fileDescriptor = await this.getStageFileDescriptor(this.audioSource); 150 await this.CreateMusicRenderer(this.audioRendererOptionsMusic); 151 try { 152 this.audioCapturerNormal = await audio.createAudioCapturer(this.audioCapturerOptionNormal); 153 console.log('ParallelCapturer,Normal capturer successs'); 154 this.audioCapturerScreen = await audio.createAudioCapturer(this.audioCapturerOptionScreen); 155 console.log('ParallelCapturer,Screen capturer successs'); 156 157 this.bufferSizeNormal = await this.audioCapturerNormal.getBufferSize(); 158 this.bufferSizeScreen = await this.audioCapturerScreen.getBufferSize(); 159 // recorder init 160 this.recordState = 'init'; 161 } catch (err) { 162 let error = err as BusinessError; 163 console.log(`ParallelCapturer:createAudioCapturer err=${JSON.stringify(error)}`); 164 } 165 } 166 167 async releseResource(): Promise<void> { 168 if (this.interval !== 0) { 169 clearInterval(this.interval); 170 } 171 if (this.fdList[NORMAL_INDEX] > 0) { 172 this.closeFile(this.fdList[NORMAL_INDEX]); 173 this.fdList[NORMAL_INDEX] = 0; 174 } 175 176 if (this.fdList[SCREEN_INDEX] > 0) { 177 this.closeFile(this.fdList[SCREEN_INDEX]); 178 this.fdList[SCREEN_INDEX] = 0; 179 } 180 181 if (this.audioCapturerNormal) { 182 await this.audioCapturerNormal.release(); 183 console.log('ParallelCapturer,audioCapturerNormal.release success'); 184 this.audioCapturerNormal = undefined; 185 } 186 187 if (this.audioCapturerScreen) { 188 await this.audioCapturerScreen.release(); 189 console.log('ParallelCapturer,audioCapturerScreen.release success'); 190 this.audioCapturerScreen = undefined; 191 } 192 193 if (this.fileDescriptor) { 194 await this.closeResource('test1.wav'); 195 this.fileDescriptor = undefined; 196 } 197 198 if (this.audioRendererMusic) { 199 await this.audioRendererMusic.release(); 200 this.audioRendererMusic = undefined; 201 } 202 for (let index = 0; index < this.audioRenderers.length; index++) { 203 await this.audioRenderers[index].release(); 204 } 205 this.audioRenderers = []; 206 } 207 208 async aboutToDisappear(): Promise<void> { 209 console.log('ParallelCapturer aboutToDisappear'); 210 await this.releseResource(); 211 } 212 213 async openFile(path: string): Promise<number | undefined> { 214 try { 215 await fs.open(path, 0o100); 216 console.log('ParallelCapturer,file created success'); 217 } catch (err) { 218 let error = err as BusinessError; 219 console.log('ParallelCapturer,file created err:' + JSON.stringify(error)); 220 return; 221 } 222 223 let file: fs.File; 224 try { 225 file = await fs.open(path, 0o2); 226 console.log(`ParallelCapturer,file open success for read and write mode,fd=${file.fd}`); 227 return file.fd; 228 } catch (err) { 229 let error = err as BusinessError; 230 console.log('ParallelCapturer,file open err:' + JSON.stringify(error)); 231 return 0; 232 } 233 } 234 235 async closeFile(fd: number): Promise<void> { 236 try { 237 await fs.close(fd); 238 console.log('ParallelCapturer,file close success'); 239 } catch (err) { 240 let error = err as BusinessError; 241 console.log('ParallelCapturer,file close err:' + JSON.stringify(error)); 242 return; 243 } 244 } 245 246 sleep(ms: number): Promise<number> { 247 return new Promise<number>((resolve) => setTimeout(resolve, ms)); 248 } 249 250 async capturerStart(): Promise<void> { 251 if (!this.audioCapturerNormal) { 252 console.log(`ParallelCapturer:audioCapturerNormal is null`); 253 return; 254 } 255 if (!this.audioCapturerScreen) { 256 console.log(`ParallelCapturer:audioCapturerScreen is null`); 257 return; 258 } 259 if (this.renderMusicState === audio.AudioState.STATE_PREPARED) { 260 this.startMusicRenderer(); 261 } 262 await this.sleep(200); 263 264 try { 265 await this.audioCapturerNormal.start(); 266 console.log('ParallelCapturer,audioCapturerNormal start success'); 267 await this.audioCapturerScreen.start(); 268 console.log('ParallelCapturer,audioCapturerScreen start success'); 269 270 // when start,init recordSec 271 this.recordState = 'started'; 272 console.log('audioCapturer start ok'); 273 274 clearInterval(this.interval); 275 this.interval = setInterval(async () => { 276 if (this.recordSec >= TOTAL_SECOND) { 277 // over TOTAL_SECOND,need to stop automatically 278 this.capturerStop(); 279 return; 280 } 281 this.recordSec++; 282 this.showTime = this.getTimesBySecond(this.recordSec); 283 }, INTERVAL_TIME); 284 285 let titleNormal = `${this.getDate(2)}_${Math.floor(Math.random() * RANDOM_NUM)}_Normal`; 286 let pathNormal = `/data/storage/el2/base/haps/entry/files/capturer_${titleNormal}.pcm`; 287 let mode = 1; 288 this.date = this.getDate(mode); 289 let titleScreen = `${this.getDate(2)}_${Math.floor(Math.random() * RANDOM_NUM)}_Screen`; 290 let pathScreen = `/data/storage/el2/base/haps/entry/files/capturer_${titleScreen}.pcm`; 291 this.titleList[NORMAL_INDEX] = titleNormal; 292 this.titleList[SCREEN_INDEX] = titleScreen; 293 this.pathList[NORMAL_INDEX] = pathNormal; 294 this.pathList[SCREEN_INDEX] = pathScreen; 295 let fdNormal = await this.openFile(pathNormal) as number; 296 let fdScreen = await this.openFile(pathScreen) as number; 297 this.fdList[NORMAL_INDEX] = fdNormal; 298 this.fdList[SCREEN_INDEX] = fdScreen; 299 setTimeout(async () => { 300 if (this.audioCapturerNormal) { 301 await this.readCapturer(this.audioCapturerNormal, this.bufferSizeNormal, fdNormal); 302 console.log('ParallelCapturer,audioCapturerNormal readCapturer success'); 303 } 304 }, READ_TIME_OUT_NORMAL); 305 setTimeout(async () => { 306 if (this.audioCapturerScreen) { 307 await this.readCapturer(this.audioCapturerScreen, this.bufferSizeScreen, fdScreen); 308 console.log('ParallelCapturer,audioCapturerScreen readCapturer success'); 309 } 310 }, READ_TIME_OUT_SCREEN); 311 } catch (err) { 312 let error = err as BusinessError; 313 console.log(`ParallelCapturer,:audioCapturer start err=${JSON.stringify(error)}`); 314 } 315 } 316 317 async capturerStop(): Promise<void> { 318 if (this.recordSec < MIN_RECORD_SECOND || !this.audioCapturerNormal || !this.audioCapturerScreen) { 319 return; 320 } 321 try { 322 323 await this.audioCapturerNormal.stop(); 324 await this.audioCapturerScreen.stop(); 325 // when recordState is stopped 326 this.recordState = 'stopped'; 327 clearInterval(this.interval); 328 if (this.renderMusicState === audio.AudioState.STATE_RUNNING) { 329 this.pauseMusicRenderer(); 330 } 331 } catch (err) { 332 let error = err as BusinessError; 333 console.log(`ParallelCapturer,:audioCapturer stop err=${JSON.stringify(error)}`); 334 } 335 this.isRecordOver = true; 336 337 console.log(JSON.stringify(this.titleList)); 338 await this.renderCreate(); 339 } 340 341 async readCapturer(audioCapturer: audio.AudioCapturer, bufferSize: number, fd: number): Promise<void> { 342 console.log('ParallelCapturer,start readCapturer'); 343 try { 344 let startOffset = 0; 345 while (true) { 346 if (audioCapturer.state === audio.AudioState.STATE_STOPPED) { 347 console.log('ParallelCapturer,state is changed to be stopped'); 348 break; 349 } 350 let buffer = await audioCapturer.read(bufferSize, true); 351 if (fd === this.fdList[NORMAL_INDEX]) { 352 console.log('NormalCapturer:readCapturer Normal read success'); 353 } else if (fd === this.fdList[SCREEN_INDEX]) { 354 console.log('NormalCapturer:readCapturer Screen read success'); 355 } 356 357 let options: Options = { 358 offset: startOffset, 359 length: bufferSize 360 }; 361 let writenSize = await fs.write(fd, buffer, options); 362 if (fd === this.fdList[NORMAL_INDEX]) { 363 console.log('ParallelCapturer--Normal,fd===' + fd + ',writenSize=' + writenSize); 364 } else if (fd === this.fdList[SCREEN_INDEX]) { 365 console.log('ParallelCapturer--Screen,fd===' + fd + ',writenSize=' + writenSize); 366 } 367 startOffset += bufferSize; 368 } 369 } 370 catch (err) { 371 let error = err as BusinessError; 372 console.log(`ParallelCapturer,readCapturer err=${JSON.stringify(error)}`); 373 } 374 } 375 376 async renderCreate(): Promise<void> { 377 try { 378 this.audioRenderers[NORMAL_INDEX] = await audio.createAudioRenderer(this.audioRendererOptions); 379 this.audioRenderers[SCREEN_INDEX] = await audio.createAudioRenderer(this.audioRendererOptions); 380 381 this.renderStateList[NORMAL_INDEX] = this.audioRenderers[NORMAL_INDEX].state; 382 this.renderStateList[SCREEN_INDEX] = this.audioRenderers[SCREEN_INDEX].state; 383 384 this.audioRenderers[NORMAL_INDEX].on('stateChange', (state) => { 385 console.log('ParallelCapturer,renderStateList[0] is changed to ' + state); 386 this.renderStateList[NORMAL_INDEX] = state; 387 }); 388 this.audioRenderers[SCREEN_INDEX].on('stateChange', (state) => { 389 console.log('ParallelCapturer,renderStateList[1] is changed to ' + state); 390 this.renderStateList[SCREEN_INDEX] = state; 391 }); 392 } catch (err) { 393 let error = err as BusinessError; 394 console.log(`ParallelCapturer,createAudioRenderer err=${JSON.stringify(error)}`); 395 } 396 } 397 398 async renderStart(index: number): Promise<void> { 399 let bufferSize = 0; 400 try { 401 bufferSize = await this.audioRenderers[index].getBufferSize(); 402 await this.audioRenderers[index].start(); 403 } catch (err) { 404 let error = err as BusinessError; 405 console.log(`ParallelCapturer,audioRenderers start err:${JSON.stringify(error)}`); 406 } 407 408 try { 409 let stat = await fs.stat(this.pathList[index]); 410 console.log(`ParallelCapturer,index:${index} stat=${JSON.stringify(stat)}`); 411 let buf = new ArrayBuffer(bufferSize); 412 console.log(`ParallelCapturer,audioRenderer write start..........`); 413 let startOffset = this.renderStartOffsetList[index]; 414 while (startOffset <= stat.size) { 415 if (this.audioRenderers[index].state === audio.AudioState.STATE_PAUSED) { 416 break; 417 } 418 // change tag,to stop 419 if (this.audioRenderers[index].state === audio.AudioState.STATE_STOPPED) { 420 break; 421 } 422 if (this.audioRenderers[index].state === audio.AudioState.STATE_RELEASED) { 423 return 424 } 425 let options: Options = { 426 offset: startOffset, 427 length: bufferSize 428 }; 429 await fs.read(this.fdList[index], buf, options); 430 await this.audioRenderers[index].write(buf); 431 this.playSecList[index] = Math.round(startOffset / stat.size * this.recordSec); //changed 432 startOffset = startOffset + bufferSize; 433 this.renderStartOffsetList[index] = startOffset; 434 } 435 console.log(`ParallelCapturer,audioRenderer write end..........`); 436 if (this.audioRenderers[index].state === audio.AudioState.STATE_RUNNING) { 437 this.renderStartOffsetList[index] = 0; 438 await this.renderStop(index); 439 } 440 } catch (err) { 441 let error = err as BusinessError; 442 console.log(`ParallelCapturer,write err:${JSON.stringify(error)}`); 443 } 444 } 445 446 async renderPause(index: number): Promise<void> { 447 try { 448 await this.audioRenderers[index].pause(); 449 } catch (err) { 450 let error = err as BusinessError; 451 console.log(`ParallelCapturer,pause err:${JSON.stringify(error)}`); 452 } 453 } 454 455 async renderStop(index: number): Promise<void> { 456 try { 457 await this.audioRenderers[index].stop(); 458 this.renderStartOffsetList[index] = 0; 459 } catch (err) { 460 let error = err as BusinessError; 461 console.log(`ParallelCapturer,stop err:${JSON.stringify(error)}`); 462 } 463 } 464 465 async renderRelease(index: number): Promise<void> { 466 try { 467 await this.audioRenderers[index].release(); 468 } catch (err) { 469 let error = err as BusinessError; 470 console.log(`ParallelCapturer,release err:${JSON.stringify(error)}`); 471 } 472 } 473 474 formatNumber(num: number): string { 475 if (num <= 9) { 476 return '0' + num; 477 } else { 478 return '' + num; 479 } 480 } 481 482 getDate(mode: number): string { 483 let date = new Date() 484 if (mode === 1) { 485 return `${date.getFullYear()}/${this.formatNumber(date.getMonth() + 1)}/${this.formatNumber(date.getDate())}`; 486 } else { 487 return `${date.getFullYear()}${this.formatNumber(date.getMonth() + 1)}${this.formatNumber(date.getDate())}`; 488 } 489 } 490 491 getTimesBySecond(t: number): string { 492 let h = Math.floor(t / 60 / 60 % 24); 493 let m = Math.floor(t / 60 % 60); 494 let s = Math.floor(t % 60); 495 let hs = h < 10 ? '0' + h : h; 496 let ms = m < 10 ? '0' + m : m; 497 let ss = s < 10 ? '0' + s : s; 498 return `${hs}:${ms}:${ss}`; 499 } 500 501 // start music player 502 async getStageFileDescriptor(fileName: string): Promise<resourceManager.RawFileDescriptor | undefined> { 503 let fileDescriptor: resourceManager.RawFileDescriptor | undefined = undefined; 504 if (this.appContext) { 505 let mgr = this.appContext.resourceManager; 506 await mgr.getRawFd(fileName).then(value => { 507 fileDescriptor = value; 508 console.log('ParallelCapturer,case getRawFileDescriptor success fileName: ' + fileName); 509 }).catch((error: BusinessError) => { 510 console.log('ParallelCapturer,case getRawFileDescriptor err: ' + error); 511 }); 512 } 513 return fileDescriptor; 514 } 515 516 async closeResource(fileName: string): Promise<void> { 517 if (this.appContext) { 518 let mgr = this.appContext.resourceManager; 519 await mgr.closeRawFd(fileName).then(() => { 520 console.log('ParallelCapturer,case closeRawFd success fileName: ' + fileName); 521 }).catch((error: BusinessError) => { 522 console.log('ParallelCapturer,case closeRawFd err: ' + error); 523 }); 524 } 525 } 526 527 async CreateMusicRenderer(options: audio.AudioRendererOptions): Promise<void> { 528 try { 529 this.audioRendererMusic = await audio.createAudioRenderer(options); 530 this.renderMusicState = this.audioRendererMusic.state; 531 this.audioRendererMusic.on('stateChange', (state) => { 532 console.log('ParallelCapturer,renderMusicState is changed to ' + state); 533 this.renderMusicState = state; 534 }); 535 } catch (err) { 536 let error = err as BusinessError; 537 console.log(`ParallelCapturer,createAudioRenderer err=${JSON.stringify(error)}`); 538 } 539 } 540 541 async startMusicRenderer(): Promise<void> { 542 if (!this.audioRendererMusic || !this.fileDescriptor) { 543 return; 544 } 545 let bufferSize: number = 0; 546 try { 547 bufferSize = await this.audioRendererMusic.getBufferSize(); 548 await this.audioRendererMusic.start(); 549 } catch (err) { 550 let error = err as BusinessError; 551 console.error(`ParallelCapturer,audioRenderer start : Error: ${JSON.stringify(error)}`); 552 return; 553 } 554 try { 555 let buf = new ArrayBuffer(bufferSize); 556 let start = this.fileDescriptor.offset; 557 if (this.startMusicOffset === 0) { 558 this.startMusicOffset = start; 559 } 560 let cur = this.startMusicOffset; 561 // start + this.fileDescriptor.length is end offset 562 while (cur <= start + this.fileDescriptor.length) { 563 // when render released,state is changed to STATE_RELEASED 564 if (this.audioRendererMusic.state === audio.AudioState.STATE_RELEASED) { 565 break; 566 } 567 // when render paused,state is changed to STATE_PAUSED 568 if (this.audioRendererMusic.state === audio.AudioState.STATE_PAUSED) { 569 this.startMusicOffset = cur; 570 break; 571 } 572 573 // change tag,to stop 574 if (this.audioRendererMusic.state === audio.AudioState.STATE_STOPPED) { 575 this.startMusicOffset = cur; 576 break; 577 } 578 579 let options: Options = { 580 offset: cur, 581 length: bufferSize 582 }; 583 console.log('startMusicRenderer,options=' + JSON.stringify(options)); 584 await fs.read(this.fileDescriptor.fd, buf, options); 585 await this.audioRendererMusic.write(buf); 586 // update progress 587 this.curTimeSec = this.getCurTimeSec(TOTAL_SECOND, this.fileDescriptor.length, cur - start); 588 cur += bufferSize; 589 } 590 // when audio play completed,update state to stopped 591 if (this.audioRendererMusic.state === audio.AudioState.STATE_RUNNING) { 592 await this.audioRendererMusic.stop(); 593 this.startMusicOffset = 0; 594 this.curTimeSec = TOTAL_SECOND; 595 } 596 } catch (err) { 597 let error = err as BusinessError; 598 console.error(`ParallelCapturer,audioRenderer write : Error: ${JSON.stringify(error)}`); 599 } 600 } 601 602 async pauseMusicRenderer(): Promise<void> { 603 try { 604 if (this.audioRendererMusic) { 605 await this.audioRendererMusic.pause(); 606 } 607 } catch (err) { 608 let error = err as BusinessError; 609 console.error(`ParallelCapturer,pauseMusicRenderer pause : Error: ${JSON.stringify(error)}`); 610 return; 611 } 612 } 613 614 async stopMusicRenderer(): Promise<void> { 615 try { 616 if (this.audioRendererMusic) { 617 await this.audioRendererMusic.stop(); 618 } 619 } catch (err) { 620 let error = err as BusinessError; 621 console.error(`ParallelCapturer,pauseMusicRenderer stop : Error: ${JSON.stringify(error)}`); 622 return; 623 } 624 } 625 626 getCurTimeSec(totalSec: number, totalLen: number, PastLen: number): number { 627 return Number((totalSec / totalLen * PastLen).toFixed(0)); 628 } 629 630 @Builder InitRecord() { 631 Column() { 632 Image($r('app.media.ic_record')).width(56).height(56); 633 } 634 .width('100%') 635 .height(56) 636 .position({ y: 60 }) 637 .id('parallel_start_btn') 638 .onClick(() => { 639 this.capturerStart(); 640 }); 641 } 642 643 @Builder StartedRecord() { 644 Column() { 645 Text(this.showTime).fontSize(21).fontWeight(500).margin({ bottom: 8 }); 646 }.width('100%').height(66).position({ y: 30 }); 647 648 Column() { 649 Image($r('app.media.ic_recording')).width(56).height(56); 650 } 651 .width('100%') 652 .height(56) 653 .position({ y: 60 }) 654 .id('parallel_stop_btn') 655 .onClick(() => { 656 this.capturerStop(); 657 }); 658 } 659 660 @Builder FinishedRecord() { 661 Column() { 662 Image($r('app.media.ic_record')).width(56).height(56); 663 }.width('100%').height(56).position({ y: 60 }).opacity(0.4); 664 } 665 666 build() { 667 Column() { 668 Column() { 669 Navigation() { 670 } 671 .width('100%') 672 .height('100%') 673 .hideBackButton(false) 674 .titleMode(NavigationTitleMode.Mini) 675 .title($r('app.string.AUDIO_CAPTURER')) 676 .mode(NavigationMode.Stack) 677 .backgroundColor('#F1F3F5'); 678 } 679 .id('parallel_capturer_back_btn') 680 .width('100%') 681 .height(56) 682 .onClick(async () => { 683 await router.replaceUrl({ url: 'pages/Index' }); 684 }); 685 686 Column() { 687 Tabs({ barPosition: BarPosition.Start, index: 1 }) { 688 TabContent() { 689 }.tabBar(this.TabBuilder(0, 'normal_capturer')); 690 691 TabContent() { 692 }.tabBar(this.TabBuilder(1, 'parallel_capturer')); 693 } 694 .vertical(false) 695 .barMode(BarMode.Fixed) 696 .barWidth(360) 697 .barHeight(56) 698 .animationDuration(400) 699 .onChange((index: number) => { 700 this.currentIndex = index 701 console.log(`${index}`) 702 if (this.currentIndex === 0) { 703 router.replaceUrl({ url: 'pages/NormalCapturer' }); 704 } 705 }) 706 .width('100%') 707 .height(56) 708 }.padding({ left: 12, right: 12 }); 709 710 Column() { 711 Row() { 712 Row() { 713 Image($r('app.media.ic_music')).width(48).height(48); 714 Text($r('app.string.MusicType')) 715 .fontSize(16) 716 .margin({ left: 12 }) 717 .fontFamily($r('sys.string.ohos_id_text_font_family_medium')) 718 .fontColor('#182431') 719 .fontWeight(500); 720 } 721 722 if (this.renderMusicState === audio.AudioState.STATE_RUNNING) { 723 Image($r('app.media.ic_play_y')).width(36).height(36); 724 } else { 725 Image($r('app.media.ic_pause_y')).width(36).height(36); 726 } 727 728 }.justifyContent(FlexAlign.SpaceBetween).width('100%').margin({ top: 12 }); 729 730 Row() { 731 Progress({ value: this.curTimeSec, total: TOTAL_SECOND, type: ProgressType.Linear }) 732 .color('#007DFF') 733 .value(this.curTimeSec) 734 .width('100%') 735 .height(4) 736 }.margin({ top: 24, bottom: 3 }).width('100%'); 737 738 Row() { 739 Text(this.getTimesBySecond(this.curTimeSec)) 740 .fontSize(12) 741 .fontFamily($r('sys.string.ohos_id_text_font_family_medium')) 742 .fontColor('#182431') 743 .opacity(0.6) 744 .fontWeight(400); 745 Text(this.getTimesBySecond(TOTAL_SECOND)) 746 .fontSize(12) 747 .fontFamily($r('sys.string.ohos_id_text_font_family_medium')) 748 .fontColor('#182431') 749 .opacity(0.6) 750 .fontWeight(400); 751 }.justifyContent(FlexAlign.SpaceBetween).width('100%'); 752 } 753 .id('music_player_card') 754 .height(126) 755 .width('100%') 756 .padding({ left: 12, right: 12 }) 757 .backgroundColor(Color.White) 758 .margin({ bottom: 20, top: 12 }) 759 .borderRadius(24) 760 .margin({ top: 12 }) 761 .onClick(() => { 762 if (this.renderMusicState === audio.AudioState.STATE_PREPARED) { 763 this.startMusicRenderer(); 764 } 765 if (this.renderMusicState === audio.AudioState.STATE_RUNNING) { 766 this.pauseMusicRenderer(); 767 } 768 if (this.renderMusicState === audio.AudioState.STATE_PAUSED) { 769 this.startMusicRenderer(); 770 } 771 if (this.renderMusicState === audio.AudioState.STATE_STOPPED) { 772 this.startMusicRenderer(); 773 } 774 }); 775 776 if (this.isRecordOver === true) { 777 Column() { 778 Row() { 779 Text($r('app.string.RECORD_RESULT')) 780 .fontSize(14) 781 .fontWeight(400) 782 .fontFamily($r('sys.string.ohos_id_text_font_family_medium')) 783 .opacity(0.6) 784 .id('record_result') 785 .textAlign(TextAlign.Start); 786 }.padding({ left: 12, right: 12 }).width('100%').margin({ top: 16, bottom: 8 }); 787 788 ForEach(this.titleList, (item: string, index) => { 789 Column() { 790 Row() { 791 Text(this.titleList[index as number]) 792 .fontSize(16) 793 .fontWeight(500) 794 .fontColor('#182431') 795 .fontFamily($r('sys.string.ohos_id_text_font_family_medium')); 796 if (this.renderStateList[index as number] === audio.AudioState.STATE_RUNNING) { 797 Image($r('app.media.ic_record_playing')).width(24).height(24).id('playing_state' + (index as number)); 798 } else { 799 Image($r('app.media.ic_record_paused')).width(24).height(24).id('paused_state' + (index as number)); 800 } 801 802 }.width('100%').height(24).justifyContent(FlexAlign.SpaceBetween).margin({ top: 16 }); 803 804 Row() { 805 Text(this.date) 806 .fontSize(14) 807 .fontWeight(400) 808 .fontColor('#182431') 809 .opacity(0.6) 810 .fontFamily($r('sys.string.ohos_id_text_font_family_medium')); 811 Text(this.getTimesBySecond(this.recordSec) + '') 812 .fontSize(14) 813 .fontWeight(400) 814 .fontColor('#182431') 815 .opacity(0.6) 816 .fontFamily($r('sys.string.ohos_id_text_font_family_medium')); 817 }.width('100%').height(24).justifyContent(FlexAlign.SpaceBetween).margin({ top: 4 }); 818 819 Row() { 820 Progress({ value: this.playSecList[index as number], total: this.recordSec, type: ProgressType.Linear }) 821 .color('#007DFF') 822 .value(this.playSecList[index as number]) 823 .width('100%') 824 .height(4); 825 }.margin({ top: 23, bottom: 3 }); 826 827 Row() { 828 Text(this.getTimesBySecond(this.playSecList[index as number]) + '') 829 .fontSize(12) 830 .fontFamily($r('sys.string.ohos_id_text_font_family_medium')) 831 .fontColor('#182431') 832 .opacity(0.6) 833 .fontWeight(400); 834 Text(this.getTimesBySecond(this.recordSec) + '') 835 .fontSize(12) 836 .fontFamily($r('sys.string.ohos_id_text_font_family_medium')) 837 .fontColor('#182431') 838 .opacity(0.6) 839 .fontWeight(400); 840 }.justifyContent(FlexAlign.SpaceBetween).width('100%'); 841 } 842 .width('100%') 843 .height(126) 844 .backgroundColor(Color.White) 845 .padding({ left: 12, right: 12 }) 846 .borderRadius(24) 847 .margin({ bottom: 12 }) 848 .id('record_player' + (index as number)) 849 .onClick(() => { 850 if (this.renderStateList[index as number] === audio.AudioState.STATE_PREPARED) { 851 this.renderStart(index as number); 852 } 853 if (this.renderStateList[index as number] === audio.AudioState.STATE_RUNNING) { 854 this.renderPause(index as number); 855 } 856 if (this.renderStateList[index as number] === audio.AudioState.STATE_PAUSED) { 857 this.renderStart(index as number); 858 } 859 if (this.renderStateList[index as number] === audio.AudioState.STATE_STOPPED) { 860 this.renderStart(index as number); 861 } 862 }); 863 }); 864 } 865 } 866 Row() { 867 if (this.recordState === 'init') { // init 868 this.InitRecord(); 869 } else if (this.recordState === 'started') { // started 870 this.StartedRecord(); 871 } else if (this.recordState === 'stopped') { // finished 872 this.FinishedRecord(); 873 } 874 } 875 .width('100%') 876 .position({ y: '82%' }) 877 .alignItems(VerticalAlign.Top) 878 .height(116) 879 .id('record_btn'); 880 } 881 .width('100%') 882 .height('100%') 883 .justifyContent(FlexAlign.Start) 884 .backgroundColor('#F1F3F5') 885 .padding({ left: 12, right: 12 }); 886 } 887} 888