• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2* Copyright (C) 2024 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*/
15
16import { audio } from '@kit.AudioKit';
17import { fileIo } from '@kit.CoreFileKit';
18import { BusinessError } from '@kit.BasicServicesKit';
19import { resourceManager } from '@kit.LocalizationKit';
20import { common } from '@kit.AbilityKit';
21import { avSession, AVCastPicker } from '@kit.AVSessionKit';
22
23class Options {
24  public offset: number = 0;
25  public length: number = 0;
26}
27
28@Entry
29@Component
30struct DefaultPicker {
31  @State session: avSession.AVSession | undefined = undefined;
32  @State avCastPickerColor:Color = Color.White;
33  private appContext?: common.Context | undefined = undefined;
34  private audioRenderer: audio.AudioRenderer | undefined = undefined;
35  private audioSource = 'test1.wav';
36  private fileDescriptor?: resourceManager.RawFileDescriptor | undefined = undefined;
37  private audioManager: audio.AudioManager | undefined = undefined;
38  private audioRoutingManager: audio.AudioRoutingManager | undefined = undefined;
39  private audioRendererInfo: audio.AudioRendererInfo = {
40    usage: audio.StreamUsage.STREAM_USAGE_VOICE_COMMUNICATION,
41    rendererFlags: 0
42  };
43  private audioStreamInfo: audio.AudioStreamInfo = {
44    samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_48000, // 采样率
45    channels: audio.AudioChannel.CHANNEL_2, // 通道
46    sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, // 采样格式
47    encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW // 编码格式
48  };
49  private  audioRendererOption: audio.AudioRendererOptions = {
50    streamInfo: this.audioStreamInfo,
51    rendererInfo: this.audioRendererInfo
52  };
53
54  async aboutToAppear() {
55    console.log('about to appear');
56    await this.init();
57  }
58
59  async init() {
60    if (!this.appContext) {
61      this.appContext = getContext();
62    }
63    this.session = await avSession.createAVSession(this.appContext, 'voiptest', 'voice_call');
64    this.observerDevices();
65  }
66
67  async observerDevices() {
68    this.audioManager = audio.getAudioManager();
69    if (!this.audioManager) {
70      console.error('get audioManager failed');
71      return;
72    }
73    this.audioRoutingManager = this.audioManager.getRoutingManager();
74    if(!this.audioRoutingManager) {
75      return;
76    }
77    this.audioRoutingManager.on('preferOutputDeviceChangeForRendererInfo', this.audioRendererInfo, (desc: audio.AudioDeviceDescriptors) => {
78      console.log(`device change to: ${desc[0].deviceType}`);
79    });
80  }
81
82  async getStageFileDescriptor(fileName: string): Promise<resourceManager.RawFileDescriptor | undefined> {
83    let fileDescriptor: resourceManager.RawFileDescriptor | undefined = undefined;
84    if (this.appContext) {
85      let mgr = this.appContext.resourceManager;
86      this.fileDescriptor = mgr.getRawFdSync(fileName);
87      await mgr.getRawFd(fileName).then(value => {
88        fileDescriptor = value;
89        console.log('case getRawFileDescriptor success fileName: ' + fileName);
90      }).catch((error: BusinessError) => {
91        console.log('case getRawFileDescriptor err: ' + error);
92      });
93    }
94    return fileDescriptor;
95  }
96
97  async startRenderer(): Promise<void> {
98    if (this.audioRenderer !== undefined) {
99      return;
100    }
101    this.getStageFileDescriptor(this.audioSource).then((res) => {
102      this.fileDescriptor = res;
103    });
104    if (!this.fileDescriptor) {
105      return;
106    }
107    let file: resourceManager.RawFileDescriptor = this.fileDescriptor;
108    try {
109      this.audioRenderer = await audio.createAudioRenderer(this.audioRendererOption);
110    } catch (error) {
111      console.error(`audioRenderer create : Error: ${JSON.stringify(error)}`);
112      return;
113    }
114    let bufferSize: number = this.fileDescriptor.offset;
115    let writeDataCallback = (buffer: ArrayBuffer) => {
116      let options: Options = {
117        offset: bufferSize,
118        length: buffer.byteLength
119      }
120      fileIo.readSync(file.fd, buffer, options);
121      bufferSize += buffer.byteLength;
122    };
123    this.audioRenderer.on('writeData', writeDataCallback);
124    await this.audioRenderer.start();
125  }
126
127  async stopRenderer(): Promise<void> {
128    if (this.audioRenderer) {
129      await this.audioRenderer.release();
130      this.audioRenderer = undefined;
131    }
132    if (this.fileDescriptor) {
133      this.closeResource(this.audioSource);
134      this.fileDescriptor = undefined;
135    }
136  }
137
138  async closeResource(fileName: string): Promise<void> {
139    if (this.appContext) {
140      let mgr = this.appContext.resourceManager;
141      await mgr.closeRawFd(fileName).then(() => {
142        console.log('case closeRawFd success fileName: ' + fileName);
143      }).catch((error: BusinessError) => {
144        console.log('case closeRawFd err: ' + error);
145      });
146    }
147  }
148
149  onBackPress(): void {
150    this.stopRenderer();
151    this.destroy();
152  }
153
154  async onPageHide(): Promise<void> {
155    this.stopRenderer();
156    this.destroy();
157  }
158
159  onPageShow(): void {
160    this.init();
161  }
162
163  destroy(): void {
164    this.appContext = undefined;
165    if (this.audioRoutingManager !== undefined) {
166      this.audioRoutingManager.off('preferOutputDeviceChangeForRendererInfo');
167    }
168    try {
169      if (this.session) {
170        this.session?.destroy();
171      }
172    } catch (err) {
173      console.error('session destroy failed');
174    }
175  }
176
177  aboutToDisappear() {
178    console.log('about to disappear');
179    this.destroy();
180  }
181
182  build() {
183    Row() {
184      Column() {
185        Button() {
186          Text('start').fontSize(22).fontColor(Color.White)
187        }
188        .size({ width: 64, height: 64 })
189        .onClick(() => {
190          this.startRenderer();
191        })
192        .type(ButtonType.Circle)
193      }
194      .size({ width: '33%', height: 64 })
195
196      Column() {
197        Button() {
198          Text('stop').fontSize(22).fontColor(Color.White)
199        }
200        .size({ width: 64, height: 64 })
201        .onClick(() => {
202          this.stopRenderer();
203        })
204        .type(ButtonType.Circle)
205      }
206      .size({ width: '33%', height: 64 })
207
208      Column() {
209        Button() {
210          AVCastPicker({
211            normalColor: this.avCastPickerColor,
212            activeColor: this.avCastPickerColor,
213          })
214            .size({ width: 45, height: 45 })
215        }
216        .size({ width: 64, height: 64 })
217        .type(ButtonType.Circle)
218      }
219      .size({ width: '33%', height: 64 })
220    }
221    .margin({ top: 300})
222    .justifyContent(FlexAlign.SpaceBetween)
223  }
224}