1# 使用通话设备切换组件 2<!--Kit: AVSession Kit--> 3<!--Subsystem: Multimedia--> 4<!--Owner: @ccfriend; @liao_qian--> 5<!--Designer: @ccfriend--> 6<!--Tester: @chenmingxi1_huawei--> 7<!--Adviser: @zengyawen--> 8 9## 切换通话输出设备 10 11系统不再提供音频输出设备切换的API,如果需要应用内切换音频输出设备,请实现AVCastPicker组件,相关参数可参考[@ohos.multimedia.avCastPicker](../../reference/apis-avsession-kit/ohos-multimedia-avcastpicker.md) 和 [@ohos.multimedia.avCastPickerParam](../../reference/apis-avsession-kit/js-apis-avCastPickerParam.md)。 12 13本文将主要介绍AVCastPicker组件接入,实现通话输出设备切换功能。 14 15当前系统支持两种组件样式的显示方式:默认样式显示和自定义样式显示。 16- 如果应用选择显示默认样式,当设备切换时,系统将根据当前选择的设备显示系统默认的组件样式。 17- 如果应用选择显示自定义样式,那么需要应用根据设备的变化刷新自己定义的样式。 18 19### 默认样式实现 20 211. 创建voice_call类型的AVSession,AVSession在构造方法中支持不同的类型参数,由AVSessionType定义,voice_call表示通话类型,如果不创建,将显示空列表。 22 23 ```ts 24 import { avSession } from '@kit.AVSessionKit'; 25 @Entry 26 @Component 27 struct Index { 28 @State message: string = 'hello world'; 29 30 build() { 31 Column() { 32 Text(this.message) 33 .onClick(async ()=> { 34 try { 35 let context = this.getUIContext().getHostContext() as Context; 36 // 通话开始时创建voice_call类型的avsession。 37 let session: AVSessionManager.AVSession = await AVSessionManager.createAVSession(context, 'voiptest', 'voice_call'); 38 } catch (err) { 39 console.error(`avsession create : Error: ${JSON.stringify(err)}`); 40 } 41 }) 42 } 43 .width('100%') 44 .height('100%') 45 } 46 } 47 ``` 48 492. 在需要切换设备的通话界面创建AVCastPicker组件。 50 51 ```ts 52 import { AVCastPicker } from '@kit.AVSessionKit'; 53 54 // 创建组件,并设置大小。 55 build() { 56 Row() { 57 Column() { 58 AVCastPicker() 59 .size({ height:45, width:45 }) 60 } 61 } 62 } 63 ``` 64 653. 创建VOICE_COMMUNICATION类型的AudioRenderer,并开始播放。具体通话音频播放等实现,请参考[AudioKit开发音频通话功能](../audio/audio-call-development.md)。 66 67 ```ts 68 import { audio } from '@kit.AudioKit'; 69 import { BusinessError } from '@kit.BasicServicesKit'; 70 71 private audioRenderer: audio.AudioRenderer | undefined = undefined; 72 private audioStreamInfo: audio.AudioStreamInfo = { 73 // 请按照实际场景设置,当前参数仅参考。 74 samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_48000, // 采样率。 75 channels: audio.AudioChannel.CHANNEL_2, // 通道。 76 sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, // 采样格式。 77 encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW // 编码格式。 78 } 79 private audioRendererInfo: audio.AudioRendererInfo = { 80 // 需使用通话场景相应的参数。 81 usage: audio.StreamUsage.STREAM_USAGE_VIDEO_COMMUNICATION, // 音频流使用类型:VOIP视频通话,默认为扬声器。 82 rendererFlags: 0 // 音频渲染器标志:默认为0即可。 83 } 84 private audioRendererOptions: audio.AudioRendererOptions = { 85 streamInfo: this.audioStreamInfo, 86 rendererInfo: this.audioRendererInfo 87 } 88 89 // 初始化,创建通话audiorenderer实例,设置监听事件。 90 try { 91 this.audioRenderer = await audio.createAudioRenderer(this.audioRendererOptions); 92 } catch (err) { 93 console.error(`audioRender create : Error: ${JSON.stringify(err)}`); 94 } 95 96 this.audioRenderer?.start((err: BusinessError) => { 97 if (err) { 98 console.error(`audioRenderer start failed -Code : ${err.code}, Message ${err.message}`); 99 } else { 100 console.info('audioRender start success'); 101 } 102 }); 103 ``` 104 1054. (可选)如果应用想知道设备切换情况,可以监听当前发声设备切换回调。 106 107 ```ts 108 import { audio } from '@kit.AudioKit'; 109 110 let audioManager = audio.getAudioManager(); // 先创建audiomanager。 111 let audioRoutingManager = audioManager.getRoutingManager(); // 再调用AudioManager的方法创建AudioRoutingManager实例。 112 113 // 可选监听当前发声设备切换回调。 114 audioRoutingManager.on('preferOutputDeviceChangeForRendererInfo', this.audioRendererInfo, (desc: audio.AudioDeviceDescriptors) => { 115 console.info(`device change To : ${desc[0].deviceType}`); // 设备类型。 116 }); 117 ``` 118 1195. 通话结束后,销毁会话。 120 121 ```ts 122 // 通话结束销毁第一步创建的session。 123 this.session?.destroy((err) => { 124 if (err) { 125 console.error(`Failed to destroy session. Code: ${err.code}, message: ${err.message}`); 126 } else { 127 console.info(`Destroy : SUCCESS `); 128 } 129 }); 130 ``` 131 132### 自定义样式实现 133 134自定义样式通过设置[CustomBuilder](../../reference/apis-arkui/arkui-ts/ts-types.md#custombuilder8)类型的参数[customPicker](../../reference/apis-avsession-kit/ohos-multimedia-avcastpicker.md#avcastpicker)实现。 135 136实现自定义样式的步骤与实现默认样式基本相同,开发者可参考[默认样式实现](#默认样式实现),完成创建AVSession、实现音频播放等步骤。 137 138存在差异的步骤如下所示。 139 1401. 创建自定义AVCastPicker,需要新增自定义参数。(对应默认样式实现步骤2) 141 142 ```ts 143 import { AVCastPicker } from '@kit.AVSessionKit'; 144 145 @State pickerImage:ResourceStr = $r('app.media.earpiece'); // 自定义资源。 146 147 build() { 148 Row() { 149 Column() { 150 AVCastPicker( 151 { 152 customPicker: (): void => this.ImageBuilder() // 新增自定义参数。 153 } 154 ).size({ height: 45, width:45 }) 155 } 156 } 157 } 158 159 // 自定义内容。 160 @Builder 161 ImageBuilder(): void { 162 Image(this.pickerImage) 163 .size({ width: '100%', height: '100%' }) 164 .backgroundColor('#00000000') 165 .fillColor(Color.Black) 166 } 167 ``` 168 1692. 如果应用要根据出声设备变化而改变自定义样式,必须监听设备切换,然后实时刷新自定义样式。(对应默认样式实现步骤4) 170 171 ```ts 172 import { audio } from '@kit.AudioKit'; 173 174 async observerDevices() { 175 let audioManager = audio.getAudioManager(); 176 let audioRoutingManager = audioManager.getRoutingManager(); 177 178 // 初次拉起AVCastPicker时需获取当前设备,刷新显示。 179 this.changePickerShow(audioRoutingManager.getPreferredOutputDeviceForRendererInfoSync(this.audioRendererInfo)); 180 181 // 监听当前发声设备切换,及时根据不同设备类型显示不同的样式。 182 audioRoutingManager.on('preferOutputDeviceChangeForRendererInfo', this.audioRendererInfo, (desc: audio.AudioDeviceDescriptors) => { 183 this.changePickerShow(audioRoutingManager.getPreferredOutputDeviceForRendererInfoSync(this.audioRendererInfo)); 184 }); 185 } 186 187 // 设备更新后刷新自定义资源pickerImage。 188 private changePickerShow(desc: audio.AudioDeviceDescriptors) { 189 if(!desc || !desc.length || !desc[0]) { 190 return; 191 } 192 if (desc[0].deviceType === 2) { 193 this.pickerImage = $r('app.media.sound'); 194 } else if (desc[0].deviceType === 7) { 195 this.pickerImage = $r('app.media.bluetooth'); 196 } else { 197 this.pickerImage = $r('app.media.earpiece'); 198 } 199 } 200 ``` 201 202## 切换通话输入设备(仅在PC/2in设备可用) 203 204系统不再提供音频输入设备切换的API,如果需要在应用内切换音频输入设备,并实现AVInputCastPicker组件,相关参数可参考[@ohos.multimedia.avInputCastPicker](../../reference/apis-avsession-kit/ohos-multimedia-avinputcastpicker.md) 和 [@ohos.multimedia.avCastPickerParam](../../reference/apis-avsession-kit/js-apis-avCastPickerParam.md)。 205 206本文将主要介绍AVInputCastPicker组件接入,实现通话输入设备切换功能。 207 208当前系统支持两种组件样式的显示方式:默认样式显示和自定义样式显示。 209- 如果应用选择显示默认样式,当设备切换时,系统将根据当前选择的设备显示系统默认的组件样式。 210- 如果应用选择显示自定义样式,那么需要应用根据设备的变化刷新自己定义的样式。 211 212### 默认实现方式 213 2141. 在需要切换设备的通话界面创建AVInputCastPicker组件。 215 216 ```ts 217 import { AVCastPickerState, AVInputCastPicker } from '@kit.AVSessionKit'; 218 219 // 设备列表显示状态变化回调(可选)。 220 private onStateChange(state: AVCastPickerState) { 221 if (state === AVCastPickerState.STATE_APPEARING) { 222 console.info('The picker starts showing.'); 223 } else if (state === AVCastPickerState.STATE_DISAPPEARING) { 224 console.info('The picker finishes presenting.'); 225 } 226 } 227 228 // 创建组件,并设置大小。 229 build() { 230 Row() { 231 Column() { 232 AVInputCastPicker( 233 { 234 onStateChange: this.onStateChange 235 } 236 ).size({ height:45, width:45 }) 237 } 238 } 239 } 240 ``` 241 2422. 实现通话功能,请参考[AudioKit开发音频通话功能](../audio/audio-call-development.md)。 243 244### 自定义实现方式 245 246自定义样式通过设置[AVInputCastPicker](../../reference/apis-avsession-kit/ohos-multimedia-avinputcastpicker.md#avinputcastpicker)中的参数customPicker实现。 247 2481. 创建自定义AVInputCastPicker,需要新增自定义参数。 249 250 ```ts 251 import { AVCastPickerState, AVInputCastPicker } from '@kit.AVSessionKit'; 252 253 @State pickerImage: ResourceStr = $r('app.media.earpiece'); // 自定义资源。 254 255 // 设备列表显示状态变化回调(可选)。 256 private onStateChange(state: AVCastPickerState) { 257 if (state === AVCastPickerState.STATE_APPEARING) { 258 console.info('The picker starts showing.'); 259 } else if (state === AVCastPickerState.STATE_DISAPPEARING) { 260 console.info('The picker finishes presenting.'); 261 } 262 } 263 264 build() { 265 Row() { 266 Column() { 267 AVInputCastPicker( 268 { 269 customPicker: (): void => this.ImageBuilder(), // 新增自定义参数。 270 onStateChange: this.onStateChange 271 } 272 ).size({ height: 45, width:45 }) 273 } 274 } 275 } 276 277 // 自定义内容。 278 @Builder 279 ImageBuilder(): void { 280 Image(this.pickerImage) 281 .size({ width: '100%', height: '100%' }) 282 .backgroundColor('#00000000') 283 .fillColor(Color.Black) 284 } 285 ``` 286 2872. 实现通话功能,请参考[AudioKit开发音频通话功能](../audio/audio-call-development.md)。