HarmonyOS鸿蒙Next中h5和鸿蒙混合开发的音频播放切换输出设备问题

HarmonyOS鸿蒙Next中h5和鸿蒙混合开发的音频播放切换输出设备问题 就是当前页面是聊天页面类似于微信聊天页面,可以发语音,是h5编写的,播放也是h5播放的,有个功能长按语音可以切换输出设备为听筒或扬声器,但是由于是h5播放的,鸿蒙侧获取不到音频流,并且h5调用鸿蒙侧更改输出设备是需要改完就是整个应用语音都是这个输出设备了,所以AudioRenderer好像没法用,但是目前项目api是16,AudioSessionManager的setDefaultOutputDevice是20以后才可以用的,有其他的方法解决这个问题吗?

5 回复

开发者您好,请查看如下方案,如方案无法解决问题,请及时反馈:

【解决方案】

请参考案例H5页面中实现扬声器听筒切换

其通过setDefaultOutputDevice(api12开始使用)设置默认发声设备,但仅适用于StreamUsage为语音消息、VoIP语音通话或者VoIP视频通话的场景,支持听筒、扬声器和系统默认设备;

AudioRenderer用于音频输出,仅支持PCM格式,需要应用持续写入音频数据进行工作。

案例使用测试PCM数据,可以按需替换为其他音频数据源。

更多关于HarmonyOS鸿蒙Next中h5和鸿蒙混合开发的音频播放切换输出设备问题的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


那就是说鸿蒙现在的没有可以强制直接更改输出设备的接口,只有有音频流或者是鸿蒙原生播放才可以

开发者您好,如果您希望提供直接更改输出设备的接口,需要您项目升级到API20以上使用AudioSessionManager的setDefaultOutputDevice接口,设备也需要升级到最新版本。使用如下demo可以在H5中切换音频输出设备:

// Index.ets
import {AVCastPicker} from '@kit.AVSessionKit';
import common from '@ohos.app.ability.common';
import { webview } from '@kit.ArkWeb';
import { audio } from '@kit.AudioKit';
import { BusinessError } from '@ohos.base';

let audioManager = audio.getAudioManager();
// 创建音频会话管理器
let audioSessionManager: audio.AudioSessionManager = audioManager.getSessionManager();
audioSessionManager.setAudioSessionScene(audio.AudioSessionScene.AUDIO_SESSION_SCENE_VOICE_COMMUNICATION);
let strategy: audio.AudioSessionStrategy = {
  concurrencyMode: audio.AudioConcurrencyMode.CONCURRENCY_MIX_WITH_OTHERS
};
audioSessionManager.activateAudioSession(strategy).then(() => {
  console.info('音频会话激活成功');
}).catch((err: BusinessError) => {
  console.error(`激活失败: ${err.code}, ${err.message}`);
});

class AudioSessionManager {
  static setDefaultOutputDevice() {
    throw new Error("Method not implemented.");
  }

  // 扬声器播放
  async setAudioDeviceToSpeaker() {
    audioSessionManager.setDefaultOutputDevice(audio.DeviceType.SPEAKER).then(() => {
      console.info('setDefaultOutputDevice to Speaker Success!');
    }).catch((err: BusinessError) => {
      console.error(`setDefaultOutputDevice Fail: ${err}`);
    });
  }

  // 听筒播放
  async setAudioDeviceToEarpiece() {
    audioSessionManager.setDefaultOutputDevice(audio.DeviceType.EARPIECE).then(() => {
      console.info('setDefaultOutputDevice to Earpiece Success!');
    }).catch((err: BusinessError) => {
      console.error(`setDefaultOutputDevice to Earpiece Fail: ${err}`);
    });
  }

  // 默认播放
  async setAudioDeviceToDefault() {
    audioSessionManager.setDefaultOutputDevice(audio.DeviceType.DEFAULT).then(() => {
      console.info('setDefaultOutputDevice to Default Success!');
    }).catch((err: BusinessError) => {
      console.error(`setDefaultOutputDevice to Default Fail: ${err}`);
    });
  }
}

@Entry
@Component
struct Index {
  // Web控制器
  private webController: webview.WebviewController = new webview.WebviewController();
  deviceManager: AudioSessionManager = new AudioSessionManager();
  context = this.getUIContext().getHostContext() as common.UIAbilityContext;

  build() {
    Column() {
      // 加载本地H5页面,audio.html在目录resources/rawfile下
      Web({ src: $rawfile('audio.html'), controller: this.webController })
        .width('100%')
        .height('80%')
        .fileAccess(true)
        .javaScriptProxy({
          object: this.deviceManager, // 暴露对象名称
          name: 'audioDevice', // H5调用标识
          methodList: ['setAudioDeviceToSpeaker', 'setAudioDeviceToEarpiece'],
          controller: this.webController
        });

      Button('切换为听筒播放').onClick(() => {
        try {
          this.deviceManager.setAudioDeviceToEarpiece();
        } catch (error) {
          console.error(`Failed to switch device: ${error.message}`);
        }
      });
      Button('切换为扬声器播放').onClick(() => {
        try {
          this.deviceManager.setAudioDeviceToSpeaker();
        } catch (error) {
          console.error(`Failed to switch device: ${error.message}`);
        }
      });
    }
    .width('100%')
    .height('100%');
  }
}
<!-- audio.html -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Audio Demo</title>
    <style>
        body { font-family: sans-serif; padding: 20px; }
        audio { width: 100%; margin-top: 20px; }
    </style>
</head>
<body>
<h2>鸿蒙 H5 音频播放示例</h2>
<p>点击下方播放按钮开始播放音乐。</p>
<audio id="myAudio" controls>
    <!-- 替换为你的实际音频 URL -->
    <source src="http://xxxx.mp3" type="audio/mpeg">
</audio>
<button type="button" onclick="switchDeviceSpeaker()" style="width: 450px; height: 120px;font-size: 50px;">切扬声器</button>
<p id="demo"></p>
<script>
    function switchDeviceSpeaker() {
      let str = audioDevice.setAudioDeviceToSpeaker();
      document.getElementById("demo").innerHTML = str;
    }
</script>

<button type="button" onclick="setAudioDeviceToEarpiece()" style="width: 450px; height: 120px;font-size: 50px;">切听筒</button>
<p id="demo1"></p>
<script>
    function setAudioDeviceToEarpiece() {
      let str = audioDevice.setAudioDeviceToEarpiece();
      document.getElementById("demo1").innerHTML = str;
    }
</script>
<script>
    const audio = document.getElementById('myAudio');
    audio.addEventListener('play', () => {
    console.log('H5 音频开始播放');
    });
    audio.addEventListener('pause', () => {
    console.log('H5 音频暂停');
    });
</script>
</body>
</html>

在HarmonyOS鸿蒙Next中,H5与鸿蒙混合开发时,音频播放切换输出设备需使用鸿蒙音频管理接口。通过AudioManager获取音频设备列表,调用selectOutputDevice方法切换设备。H5页面可通过鸿蒙提供的JS桥接能力调用原生音频管理模块,实现设备切换功能。

针对HarmonyOS Next中H5与鸿蒙混合开发场景下的音频输出设备切换问题,当前确实存在挑战。由于H5音频播放由Web引擎内部管理,鸿蒙侧无法直接通过AudioRenderer控制其音频流。

在API 16的条件下,可以考虑以下技术思路:

  1. 统一音频路由管理:在鸿蒙侧建立全局音频输出设备状态管理。当H5需要切换设备时(如通过ArkTS与Web组件的postMessage通信),鸿蒙侧记录设备偏好(如听筒/扬声器),并强制重新初始化后续所有音频播放会话。这需要H5在每次播放前向鸿蒙侧查询当前设备设置,并可能伴随短暂延迟。

  2. Native桥接播放:对于关键音频(如语音消息),可考虑在用户点击播放时,由H5将音频数据或URI通过桥接传递给鸿蒙侧,使用AudioRenderer进行原生播放。这样即可通过AudioRenderersetOutputDevice方法(API 9+支持)精确控制该条语音的输出设备。这需要额外的数据传输与组件开发。

  3. 场景化设备预测:结合传感器(如接近光传感器)或UI上下文(如将手机贴近耳朵),由鸿蒙侧动态预测并切换全局音频输出模式。H5播放音频时,实际上遵从了系统级的设备切换,但此方法非精确控制。

目前,在API 16下尚无系统API能直接、动态地指定单个H5音频流的输出设备。建议评估将关键音频播放原生化的可行性,或规划升级至API 20+以使用AudioSessionManager进行更细粒度的管理。

回到顶部