如何在H5中调用麦克风
===========

#### 问题现象

WebView网页端获取麦克风录音权限时，没有成功开启录音。

#### 背景知识

*   [声明权限](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/declare-permissions)：应用在申请权限时，需要在项目的配置文件中，逐个声明需要的权限，否则应用将无法获取授权。
*   [onPermissionRequest](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-basic-components-web-events#onpermissionrequest9)：通知收到获取权限请求，需配置"ohos.permission.CAMERA"、"ohos.permission.MICROPHONE"权限。

#### 解决方案

1.  在module.json5文件配置'ohos.permission.MICROPHONE'话筒权限。
    
    {
      "module": {
       _ // ..._
        "requestPermissions":\[
          {
            "name" : "ohos.permission.MICROPHONE",
            "reason": "$string:micro\_reason",
            "usedScene": {
              "abilities": \[
                "FormAbility"
              \],
              "when":"inuse"
            }
          },
        \]
      }
    }
    
2.  Web组件通过onPermissionRequest方法获取话筒权限请求。
    
    import { webview } from '@kit.ArkWeb';
    import { BusinessError } from '@kit.BasicServicesKit';
    import { abilityAccessCtrl } from '@kit.AbilityKit';
    
    @Entry
    @Component
    struct Index {
      controller: webview.WebviewController \= new webview.WebviewController();
    
      aboutToAppear() {
        let atManager \= abilityAccessCtrl.createAtManager();
        _// 通过onPermissionRequest获取权限请求_
        atManager.requestPermissionsFromUser(this.getUIContext().getHostContext(), \['ohos.permission.MICROPHONE'\])
          .then((data) \=\> {
            console.info(\`data: ${JSON.stringify(data)}\`);
            console.info(\`data permissions: ${data.permissions}\`);
            console.info(\`data authResults: ${data.authResults}\`);
    
          }).catch((error: BusinessError) \=\> {
          console.error(\`Failed to request permissions from user. Code is ${error.code}, message is ${error.message}\`);
        });
      }
    
      build() {
        Column() {
          Web({ src: $rawfile('index.html'), controller: this.controller })
            .geolocationAccess(false)
            .javaScriptAccess(true)
            .fileAccess(true)
            .domStorageAccess(true)
            .onPermissionRequest((event) \=\> {
              if (event) {
                this.getUIContext().showAlertDialog({
                  title: '麦克风权限请求',
                  message: '是否允许获取麦克风权限',
                  primaryButton: {
                    value: 'deny',
                    action: () \=\> {
                      event.request.deny();
                    }
                  },
                  secondaryButton: {
                    value: 'onConfirm',
                    action: () \=\> {
                      event.request.grant(event.request.getAccessibleResource());
                    }
                  },
                  cancel: () \=\> {
                    event.request.deny();
                  }
                });
              }
            })
        }
      }
    }
    
    <!DOCTYPE html\>
    <html\>
    <head\>
        <title\>测试网页</title\>
        <meta name\="viewport" content\="initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no"\>
    </head\>
    <body\>
    <button id\="recordButton"\>录音</button\>
    <button id\="stopButton" disabled\>停止</button\>
    <audio id\="player" controls preload\="auto"\></audio\>
    </body\>
    <script\>
        const recordButton = document.getElementById('recordButton');
        const stopButton = document.getElementById('stopButton');
        const player = document.getElementById('player');
        let chunks = \[\];
        navigator.mediaDevices
            .getUserMedia({ audio: true })
            .then((stream) => {
                const mediaRecorder = new MediaRecorder(stream);
                recordButton.onclick = () => {
                    mediaRecorder.start();
                    stopButton.disabled = false;
                    recordButton.disabled = true;
                };
        
                stopButton.onclick = () => {
                    mediaRecorder.stop();
                    recordButton.disabled = false;
                    stopButton.disabled = true;
                };
        
                mediaRecorder.onstop = (e) => {
                    player.controls = true;
                    const blob = new Blob(chunks, { type: 'audio/mpeg-3' });
                    chunks = \[\];
                    const audioURL = URL.createObjectURL(blob);
                    player.src = audioURL;
                    player.load();
                };
        
                mediaRecorder.ondataavailable = (e) => {
                    chunks.push(e.data);
                };
            }).catch((err) => {
            console.error(\`The following error occurred: ${err}\`);
        });
        player.onloadedmetadata = e => {
            const audio = e.target;
            const audioDuration = audio.duration;
            if (audioDuration === Infinity) {
                audio.currentTime = 1e101;
                audio.ontimeupdate = function () {
                    this.ontimeupdate = () => {
                        return;
                    };
                    audio.currentTime = 1e101;
                    audio.currentTime = 0;
                };
            }
        };
    </script\>
    </html\>
    

#### 常见FAQ

Q：调用getUserMedia()接口无响应或返回权限拒绝错误。

A：非安全上下文(非HTTPS)下getUserMedia()会被自动拒绝。