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