• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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