1# 使用通话设备切换组件 2 3## 基本概念 4 5系统不再提供音频输出设备切换的API,如果需要应用内切换音频输出设备,请实现AVCastPicker组件,相关参数可参考[@ohos.multimedia.avCastPicker](../../reference/apis-avsession-kit/ohos-multimedia-avcastpicker.md) 和 [@ohos.multimedia.avCastPickerParam](../../reference/apis-avsession-kit/js-apis-avCastPickerParam.md)。 6 7本文将主要介绍AVCastPicker组件接入,实现通话设备切换。 8 9当前系统支持两种组件样式的显示方式:默认样式显示和自定义样式显示。如果应用选择显示默认样式,当设备切换时,系统将根据当前选择的设备显示系统默认的组件样式;如果应用选择显示自定义样式,那么需要应用根据设备的变化刷新自己定义的样式。 10 11## 开发步骤 12 13### 默认样式实现 14 151. 创建voice_call类型的AVSession,AVSession在构造方法中支持不同的类型参数,由AVSessionType定义,voice_call表示通话类型,如果不创建,将显示空列表。 16 17 ```ts 18 import { avSession } from '@kit.AVSessionKit'; 19 @Entry 20 @Component 21 struct Index { 22 @State message: string = 'hello world'; 23 24 build() { 25 Column() { 26 Text(this.message) 27 .onClick(()=>{ 28 let context = this.getUIContext().getHostContext() as Context; 29 // 通话开始时创建voice_call类型的avsession。 30 let session: AVSessionManager.AVSession = await AVSessionManager.createAVSession(context, 'voiptest', 'voice_call'); 31 }) 32 } 33 .width('100%') 34 .height('100%') 35 } 36 } 37 ``` 38 392. 在需要切换设备的通话界面创建AVCastPicker组件。 40 41 ```ts 42 import { AVCastPicker } from '@kit.AVSessionKit'; 43 44 // 创建组件,并设置大小。 45 build() { 46 Row() { 47 Column() { 48 AVCastPicker() 49 .size({ height:45, width:45 }) 50 } 51 } 52 } 53 ``` 54 553. 创建VOICE_COMMUNICATION类型的AudioRenderer,并开始播放。具体通话音频播放等实现,请参考[AudioKit开发音频通话功能](../audio/audio-call-development.md)。 56 57 ```ts 58 import { audio } from '@kit.AudioKit'; 59 import { BusinessError } from '@kit.BasicServicesKit'; 60 61 private audioRenderer: audio.AudioRenderer | undefined = undefined; 62 private audioStreamInfo: audio.AudioStreamInfo = { 63 // 请按照实际场景设置,当前参数仅参考。 64 samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_48000, // 采样率。 65 channels: audio.AudioChannel.CHANNEL_2, // 通道。 66 sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, // 采样格式。 67 encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW // 编码格式。 68 } 69 private audioRendererInfo: audio.AudioRendererInfo = { 70 // 需使用通话场景相应的参数。 71 usage: audio.StreamUsage.STREAM_USAGE_VIDEO_COMMUNICATION, // 音频流使用类型:VOIP视频通话,默认为扬声器。 72 rendererFlags: 0 // 音频渲染器标志:默认为0即可。 73 } 74 private audioRendererOptions: audio.AudioRendererOptions = { 75 streamInfo: this.audioStreamInfo, 76 rendererInfo: this.audioRendererInfo 77 } 78 79 // 初始化,创建通话audiorenderer实例,设置监听事件。 80 try { 81 this.audioRenderer = await audio.createAudioRenderer(this.audioRendererOptions); 82 } catch (err) { 83 console.error(`audioRender create : Error: ${JSON.stringify(err)}`); 84 } 85 86 this.audioRenderer?.start((err: BusinessError) => { 87 if (err) { 88 console.error(`audioRender start faild : Error: ${JSON.stringify(err)}`); 89 } else { 90 console.error('audioRender start success'); 91 } 92 }); 93 ``` 94 954. (可选)如果应用想知道设备切换情况,可以监听当前发声设备切换回调。 96 97 ```ts 98 import { audio } from '@kit.AudioKit'; 99 100 let audioManager = audio.getAudioManager(); // 先创建audiomanager。 101 let audioRoutingManager = audioManager.getRoutingManager(); // 再调用AudioManager的方法创建AudioRoutingManager实例。 102 103 // 可选监听当前发声设备切换回调。 104 audioRoutingManager.on('preferOutputDeviceChangeForRendererInfo', this.audioRendererInfo, (desc: audio.AudioDeviceDescriptors) => { 105 console.info(`device change To : ${desc[0].deviceType}`); // 设备类型。 106 }); 107 ``` 108 1095. 通话结束后,销毁会话。 110 111 ```ts 112 // 通话结束销毁第一步创建的session。 113 this.session?.destroy((err) => { 114 if (err) { 115 console.error(`Failed to destroy session. Code: ${err.code}, message: ${err.message}`); 116 } else { 117 console.info(`Destroy : SUCCESS `); 118 } 119 }); 120 ``` 121 122### 自定义样式实现 123 124自定义样式通过设置[CustomBuilder](../../reference/apis-avsession-kit/ohos-multimedia-avcastpicker.md)类型的参数customPicker实现。 125 126实现自定义样式的步骤与实现默认样式基本相同,开发者可参考[默认样式实现](#默认样式实现),完成创建AVSession、实现音频播放等步骤。 127 128存在差异的步骤如下所示。 129 1301. 创建自定义AVCastPicker,需要新增自定义参数。(对应默认样式实现步骤2) 131 132 ```ts 133 import { AVCastPicker } from '@kit.AVSessionKit'; 134 135 @State pickerImage:ResourceStr = $r('app.media.earpiece'); // 自定义资源。 136 137 build() { 138 Row() { 139 Column() { 140 AVCastPicker( 141 { 142 customPicker: (): void => this.ImageBuilder() // 新增自定义参数。 143 } 144 ).size({ height: 45, width:45 }) 145 } 146 } 147 } 148 149 // 自定义内容。 150 @Builder 151 ImageBuilder(): void { 152 Image(this.pickerImage) 153 .size({ width: '100%', height: '100%' }) 154 .backgroundColor('#00000000') 155 .fillColor(Color.Black) 156 } 157 ``` 158 1592. 如果应用要根据出声设备变化而改变自定义样式,必须监听设备切换,然后实时刷新自定义样式。(对应默认样式实现步骤4) 160 161 ```ts 162 import { audio } from '@kit.AudioKit'; 163 164 async observerDevices() { 165 let audioManager = audio.getAudioManager(); 166 let audioRoutingManager = audioManager.getRoutingManager(); 167 168 // 初次拉起AVCastPicker时需获取当前设备,刷新显示。 169 this.changePickerShow(audioRoutingManager.getPreferredOutputDeviceForRendererInfoSync(this.audioRendererInfo)); 170 171 // 监听当前发声设备切换,及时根据不同设备类型显示不同的样式。 172 audioRoutingManager.on('preferOutputDeviceChangeForRendererInfo', this.audioRendererInfo, (desc: audio.AudioDeviceDescriptors) => { 173 this.changePickerShow(audioRoutingManager.getPreferredOutputDeviceForRendererInfoSync(this.audioRendererInfo)); 174 }); 175 } 176 177 // 设备更新后刷新自定义资源pickerImage。 178 private changePickerShow(desc: audio.AudioDeviceDescriptors) { 179 if (desc[0].deviceType === 2) { 180 this.pickerImage = $r('app.media.sound'); 181 } else if (desc[0].deviceType === 7) { 182 this.pickerImage = $r('app.media.bluetooth'); 183 } else { 184 this.pickerImage = $r('app.media.earpiece'); 185 } 186 } 187 ``` 188 189<!--RP1--> 190<!--RP1End-->