【问题标题】:Audio in audio and video HTML5 elements are muted while using WebAudio API使用 WebAudio API 时音频和视频 HTML5 元素中的音频被静音
【发布时间】:2020-12-02 19:42:37
【问题描述】:

我正在尝试从视频中获取音频以使用 Web Audio API。但是视频中的音频是静音的。当我在本地测试此代码时,HTML5 音频正在工作(在 jsfiddle 上,当 Web 音频 API 开启时它不起作用)但在本地和 jsfiddle 视频没有音频(它被静音并且用户无法更改)。控制台中没有显示错误。我添加了适用于自动播放策略更改的功能:https://developers.google.com/web/updates/2017/09/autoplay-policy-changes#webaudio。这是我的代码:

// Required by new google policy more here: https://developers.google.com/web/updates/2017/09/autoplay-policy-changes#webaudio

var context = null;

var myAudio, source, splitter, listener = null;

var FrontLeft, FrontCenter, FrontRight, SurroundLeft, SurroundRight, Sub = null;

var pannerNodesObjects = [FrontLeft, FrontCenter, FrontRight, SurroundLeft, SurroundRight, Sub];

var distanceFromScreen, screenCenterY = null;

var x_FrontLeft, y_FrontLeft, z_FrontLeft = null;
var x_FrontCenter, y_FrontCenter, z_FrontCenter = null;
var x_FrontRight, y_FrontRight, z_FrontRight = null;
var x_SurroundLeft, y_SurroundLeft, z_SurroundLeft = null;
var x_SurroundRight, y_SurroundRight, z_SurroundRight = null;
var x_Sub, y_Sub, z_Sub = null;

var web_Audio_enable = false;

var initailPosition = [[x_FrontLeft, y_FrontLeft, z_FrontLeft],
[x_FrontCenter, y_FrontCenter, z_FrontCenter],
[x_FrontRight, y_FrontRight, z_FrontRight],
[x_SurroundLeft, y_SurroundLeft, z_SurroundLeft],
[x_SurroundRight, y_SurroundRight, z_SurroundRight],
[x_Sub, y_Sub, z_Sub]];
    
// One-liner to resume playback when user interacted with the page.
function startFunction() {
      // Create splitter
      context = new AudioContext();

      // get the audio element
      myAudio = document.getElementById('video');
      
      // myAudio = document.querySelector('video');
      source = context.createMediaElementSource(myAudio);

      // var dest = context.createMediaStreamDestination();

      //Spliter channels L, R, SL, SR, C, LFE
      splitter = new ChannelSplitterNode(context, { numberOfOutputs: 6 });

      // let channel_merger = new ChannelMergerNode(context, {numberOfInputs: 2});

      listener = context.listener;

      source.connect(splitter);

      web_Audio_enable = true;
      start_function();
      console.log('Playback resumed successfully');
  }

//Estimate screen width for sound source placement
//NOTE: this is estimation it is not very accurate but for this project it is acurate enought
// Used by create_BabylonCamera
function estimate_ScreenParams() {
    var $el = document.createElement('div');
    $el.style.width = '1cm';
    $el.style.height = '1cm';
    $el.style.backgroundColor = '#ff0000';
    $el.style.position = 'fixed';
    $el.style.bottom = 0;
    document.body.appendChild($el);
    var screenHeight = window.screen.height / $el.offsetHeight;
    var screenWidth = window.screen.width / $el.offsetWidth;
    console.log("Screen Width in cm: " + screenWidth);
    console.log("Screen Height in cm: " + screenHeight);
    var screenDiagonalInches = Math.sqrt(Math.pow((window.screen.width / $el.offsetWidth), 2) + Math.pow((window.screen.height / $el.offsetHeight), 2)) / 2.54;
    console.log("Screen Diagonal in in: " + screenDiagonalInches);
    document.body.removeChild($el);
    //Screen center height in meters
    var screenCenterY = (screenHeight / 2) / 100;

    //Calculate distance form screen based on estimated screen diagonal length and resolution
    //Screen resolution
    var screenResWidth = window.screen.width * window.devicePixelRatio;
    var screenResHeight = window.screen.height * window.devicePixelRatio;

    var loc_distanceFromScreen = null;

    //distanceFromScreen will be used for initial positioning of the  Surround Left and Right speakers
    // Distance is in meters
    if (screenDiagonalInches < 14) {
        loc_distanceFromScreen = 0.61; //minimum distance
    }
    else {
        loc_distanceFromScreen = 0.61 + (Math.round(screenDiagonalInches - 14) / 2) * 0.15;
    }
    console.log("Estimated distance from screen: " + loc_distanceFromScreen);

    distanceFromScreen = loc_distanceFromScreen;

    return screenCenterY;
}

function set_pannerNode(node, panningModel /* 'HRTF' */, distanceModel /* Possible values are "linear", "inverse" and "exponential". The default value is "inverse". */, refDistance, maxDistance, rolloffFactor, coneInnerAngle, coneOuterAngle, coneOuterGain, x, y, z /* position */, n /* index of the pannerNodesObjects */) {

    node = context.createPanner();

    // Seting options
    node.panningModel = panningModel;
    node.distanceModel = distanceModel;
    node.refDistance = refDistance;
    node.maxDistance = maxDistance;
    node.rolloffFactor = rolloffFactor;
    node.coneInnerAngle = coneInnerAngle;
    node.coneOuterAngle = coneOuterAngle;
    node.coneOuterGain = coneOuterGain;

    // Setting position
    if (node.positionX) {
        node.positionX.setValueAtTime(x, context.currentTime);
        node.positionY.setValueAtTime(y, context.currentTime);
        node.positionZ.setValueAtTime(z, context.currentTime);
    } else {
        node.setPosition(x, y, z);
    }
    pannerNodesObjects[n] = node;
}

// Function to rotate 
// cx, cy, cz - global center of rotation

var temp_position = [[-0.12, 0.835, 0], /* Front left */
                     [0.12, 0.835, 0], /* Front right */
                     [0.0, 0.85, 0], /* Front center */
                     [0.08, 0.84, 0], /* Sub */
                     [-0.17, -0.83, 0], /* Surround left */
                     [0.17, -0.83, 0]]; /* Surround right */

function set_rotation(x, y, angle, n /* n is a int that says what initial position to change it is the index of the speaker 0 - FL, 1 - FC, 2 - FR etc. */) {

    /* 
    Currently not used
    */
    initailPosition[n][0] = temp_position[n][0];
    initailPosition[n][1] = temp_position[n][1];
    initailPosition[n][2] = temp_position[n][2];

    console.log(n + " x: " + initailPosition[n][0] + " y: " + initailPosition[n][1] + " z: "+initailPosition[n][2]);
    return [initailPosition[n][0], initailPosition[n][1], initailPosition[n][2]];
}

function rotate(node_obj, pitch, roll, yaw, i) {
    var cosa = Math.cos(yaw);
    var sina = Math.sin(yaw);

    var cosb = Math.cos(pitch);
    var sinb = Math.sin(pitch);

    var cosc = Math.cos(roll);
    var sinc = Math.sin(roll);

    var Axx = cosa*cosb;
    var Axy = cosa*sinb*sinc - sina*cosc;
    var Axz = cosa*sinb*cosc + sina*sinc;

    var Ayx = sina*cosb;
    var Ayy = sina*sinb*sinc + cosa*cosc;
    var Ayz = sina*sinb*cosc - cosa*sinc;

    var Azx = -sinb;
    var Azy = cosb*sinc;
    var Azz = cosb*cosc;

    px = temp_position[i][0];
    py = temp_position[i][1];
    pz = temp_position[i][2];

    node_x = Axx*px + Axy*py + Axz*pz;
    node_y = Ayx*px + Ayy*py + Ayz*pz;
    node_z = Azx*px + Azy*py + Azz*pz;

    // Setting position
    if (node_obj.positionX) {
        // node_obj.positionX.value = node_x;
        // node_obj.positionY.value = node_y;
        // node_obj.positionZ.value = node_z;
        // node_obj.positionX.setValueAtTime(node_x, context.currentTime);
        // node_obj.positionY.setValueAtTime(node_y, context.currentTime);
        // node_obj.positionZ.setValueAtTime(node_z, context.currentTime);
        node_obj.positionX.linearRampToValueAtTime(node_x, context.currentTime + 0.1);
        node_obj.positionY.linearRampToValueAtTime(node_y, context.currentTime + 0.1);
        node_obj.positionZ.linearRampToValueAtTime(node_z, context.currentTime + 0.1);
    } else {
        node_obj.setPosition(node_x, node_y, node_z);
    }
    pannerNodesObjects[i] = node_obj;
    if(i==0){
        console.log("Front Left x: " + pannerNodesObjects[i].positionX.value + " y: " + pannerNodesObjects[i].positionY.value  + " z: " + pannerNodesObjects[i].positionZ.value );
    }
    if(i==1){
        console.log("Front Right x: " + pannerNodesObjects[i].positionX.value + " y: " + pannerNodesObjects[i].positionY.value + " z: " + pannerNodesObjects[i].positionZ.value );
    }
    if(i==2){
        console.log("Front Center x: " + pannerNodesObjects[i].positionX.value + " y: " + pannerNodesObjects[i].positionY.value + " z: " + pannerNodesObjects[i].positionZ.value );
    }
    
}


// Starting function creates PannerNodes and adds parameters to them
function start_function() {

    screenCenterY = estimate_ScreenParams();

    console.log("Distance from screen in start_function: " + distanceFromScreen);
    console.log("screenCenterY in start_function: " + screenCenterY);

    if (listener.forwardX) {
        listener.forwardX.setValueAtTime(0, context.currentTime);
        listener.forwardY.setValueAtTime(0, context.currentTime);
        listener.forwardZ.setValueAtTime(-1, context.currentTime);
        listener.upX.setValueAtTime(0, context.currentTime);
        listener.upY.setValueAtTime(1, context.currentTime);
        listener.upZ.setValueAtTime(0, context.currentTime);
    } else {
        listener.setOrientation(0, 0, -1, 0, 1, 0);
    }

    var angleList = [90, -90, 0, -110, 110, -20];
    for (i = 0; i < pannerNodesObjects.length; i++) {
        [nx, ny, nz] = set_rotation(distanceFromScreen, screenCenterY, angleList[i], i);
        set_pannerNode(pannerNodesObjects[i], 'HRTF', "exponential", 1, 100, 2, 360, 0, 0, nx, ny, nz, i);
        splitter.connect(pannerNodesObjects[i], i);
        pannerNodesObjects[i].connect(context.destination);
    }
    console.log(context.destination);
    console.log("start_function Finished!");
}
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>MyTitle</title>
  <base href="/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
  <script src="https://unpkg.com/mathjs@7.6.0/dist/math.js"></script>
</head>
<body>
  <button type="button" id="start_web_audio" onclick="startFunction()">Click to allow Web Audio</button> <p>Required to start webaudio API due to changes in autoplay policy on modern browsers</p>
  
    <audio src="https://raw.githubusercontent.com/VGFP/AudioSamplesForBabylonJSProject/master/VideoDemo/V3_Voice_Only_ChID-BLITS-EBU-Narration.ogg" controls id="video" loop></audio>
  
  <!-- <video src="https://download.dolby.com/us/en/test-tones/dolby-atmos-trailer_amaze_1080.mp4" width=600 height=400 controls id="video"></video> -->

</body>
</html>

【问题讨论】:

    标签: javascript web-audio-api


    【解决方案1】:

    当创建 MediaElementSource (MES) 时,其目标 MediaElement 会将其音频流路由到 MES,这是正常行为,如果此 MES 未连接到 AudioContext 的目的地,则 MediaSource 的音频会被静音。

    但这不是这里发生的事情。

    您的问题是文件未按照Same-Origin-Policy 提供,因此不允许您的代码读取该媒体的内容,并且在 MSE 中发出声音被视为读取您脚本中的媒体(因为您可以在之后的任何地方连接此 MSE)。所以 MSE 只会产生静默。

    要使您的代码正常工作,您需要使用正确的 Access-Control-Allow-Origin 标头提供该文件,并使用 crossorigin 属性集让您的页面请求它。
    但是,download.dolby.com 并未配置为允许此类跨域访问,因此您必须将此文件托管在其他地方。 在下面的演示中,我们将使用 wikimedia.org 提供的文件,该文件允许跨域访问。

    // Required by new google policy more here: https://developers.google.com/web/updates/2017/09/autoplay-policy-changes#webaudio
    
    var context = null;
    
    var myAudio, source, splitter, listener = null;
    
    var FrontLeft, FrontCenter, FrontRight, SurroundLeft, SurroundRight, Sub = null;
    
    var pannerNodesObjects = [FrontLeft, FrontCenter, FrontRight, SurroundLeft, SurroundRight, Sub];
    
    var distanceFromScreen, screenCenterY = null;
    
    var x_FrontLeft, y_FrontLeft, z_FrontLeft = null;
    var x_FrontCenter, y_FrontCenter, z_FrontCenter = null;
    var x_FrontRight, y_FrontRight, z_FrontRight = null;
    var x_SurroundLeft, y_SurroundLeft, z_SurroundLeft = null;
    var x_SurroundRight, y_SurroundRight, z_SurroundRight = null;
    var x_Sub, y_Sub, z_Sub = null;
    
    var web_Audio_enable = false;
    
    var initailPosition = [[x_FrontLeft, y_FrontLeft, z_FrontLeft],
    [x_FrontCenter, y_FrontCenter, z_FrontCenter],
    [x_FrontRight, y_FrontRight, z_FrontRight],
    [x_SurroundLeft, y_SurroundLeft, z_SurroundLeft],
    [x_SurroundRight, y_SurroundRight, z_SurroundRight],
    [x_Sub, y_Sub, z_Sub]];
        
    // One-liner to resume playback when user interacted with the page.
    function startFunction() {
          // Create splitter
          context = new AudioContext();
    
          // get the audio element
          myAudio = document.getElementById('video');
          
          // myAudio = document.querySelector('video');
          source = context.createMediaElementSource(myAudio);
    
          // var dest = context.createMediaStreamDestination();
    
          //Spliter channels L, R, SL, SR, C, LFE
          splitter = new ChannelSplitterNode(context, { numberOfOutputs: 6 });
    
          // let channel_merger = new ChannelMergerNode(context, {numberOfInputs: 2});
    
          listener = context.listener;
    
          source.connect(splitter);
    
          web_Audio_enable = true;
          start_function();
          console.log('Playback resumed successfully');
      }
    
    //Estimate screen width for sound source placement
    //NOTE: this is estimation it is not very accurate but for this project it is acurate enought
    // Used by create_BabylonCamera
    function estimate_ScreenParams() {
        var $el = document.createElement('div');
        $el.style.width = '1cm';
        $el.style.height = '1cm';
        $el.style.backgroundColor = '#ff0000';
        $el.style.position = 'fixed';
        $el.style.bottom = 0;
        document.body.appendChild($el);
        var screenHeight = window.screen.height / $el.offsetHeight;
        var screenWidth = window.screen.width / $el.offsetWidth;
        console.log("Screen Width in cm: " + screenWidth);
        console.log("Screen Height in cm: " + screenHeight);
        var screenDiagonalInches = Math.sqrt(Math.pow((window.screen.width / $el.offsetWidth), 2) + Math.pow((window.screen.height / $el.offsetHeight), 2)) / 2.54;
        console.log("Screen Diagonal in in: " + screenDiagonalInches);
        document.body.removeChild($el);
        //Screen center height in meters
        var screenCenterY = (screenHeight / 2) / 100;
    
        //Calculate distance form screen based on estimated screen diagonal length and resolution
        //Screen resolution
        var screenResWidth = window.screen.width * window.devicePixelRatio;
        var screenResHeight = window.screen.height * window.devicePixelRatio;
    
        var loc_distanceFromScreen = null;
    
        //distanceFromScreen will be used for initial positioning of the  Surround Left and Right speakers
        // Distance is in meters
        if (screenDiagonalInches < 14) {
            loc_distanceFromScreen = 0.61; //minimum distance
        }
        else {
            loc_distanceFromScreen = 0.61 + (Math.round(screenDiagonalInches - 14) / 2) * 0.15;
        }
        console.log("Estimated distance from screen: " + loc_distanceFromScreen);
    
        distanceFromScreen = loc_distanceFromScreen;
    
        return screenCenterY;
    }
    
    function set_pannerNode(node, panningModel /* 'HRTF' */, distanceModel /* Possible values are "linear", "inverse" and "exponential". The default value is "inverse". */, refDistance, maxDistance, rolloffFactor, coneInnerAngle, coneOuterAngle, coneOuterGain, x, y, z /* position */, n /* index of the pannerNodesObjects */) {
    
        node = context.createPanner();
    
        // Seting options
        node.panningModel = panningModel;
        node.distanceModel = distanceModel;
        node.refDistance = refDistance;
        node.maxDistance = maxDistance;
        node.rolloffFactor = rolloffFactor;
        node.coneInnerAngle = coneInnerAngle;
        node.coneOuterAngle = coneOuterAngle;
        node.coneOuterGain = coneOuterGain;
    
        // Setting position
        if (node.positionX) {
            node.positionX.setValueAtTime(x, context.currentTime);
            node.positionY.setValueAtTime(y, context.currentTime);
            node.positionZ.setValueAtTime(z, context.currentTime);
        } else {
            node.setPosition(x, y, z);
        }
        pannerNodesObjects[n] = node;
    }
    
    // Function to rotate 
    // cx, cy, cz - global center of rotation
    
    var temp_position = [[-0.12, 0.835, 0], /* Front left */
                         [0.12, 0.835, 0], /* Front right */
                         [0.0, 0.85, 0], /* Front center */
                         [0.08, 0.84, 0], /* Sub */
                         [-0.17, -0.83, 0], /* Surround left */
                         [0.17, -0.83, 0]]; /* Surround right */
    
    function set_rotation(x, y, angle, n /* n is a int that says what initial position to change it is the index of the speaker 0 - FL, 1 - FC, 2 - FR etc. */) {
    
        /* 
        Currently not used
        */
        initailPosition[n][0] = temp_position[n][0];
        initailPosition[n][1] = temp_position[n][1];
        initailPosition[n][2] = temp_position[n][2];
    
        console.log(n + " x: " + initailPosition[n][0] + " y: " + initailPosition[n][1] + " z: "+initailPosition[n][2]);
        return [initailPosition[n][0], initailPosition[n][1], initailPosition[n][2]];
    }
    
    function rotate(node_obj, pitch, roll, yaw, i) {
        var cosa = Math.cos(yaw);
        var sina = Math.sin(yaw);
    
        var cosb = Math.cos(pitch);
        var sinb = Math.sin(pitch);
    
        var cosc = Math.cos(roll);
        var sinc = Math.sin(roll);
    
        var Axx = cosa*cosb;
        var Axy = cosa*sinb*sinc - sina*cosc;
        var Axz = cosa*sinb*cosc + sina*sinc;
    
        var Ayx = sina*cosb;
        var Ayy = sina*sinb*sinc + cosa*cosc;
        var Ayz = sina*sinb*cosc - cosa*sinc;
    
        var Azx = -sinb;
        var Azy = cosb*sinc;
        var Azz = cosb*cosc;
    
        px = temp_position[i][0];
        py = temp_position[i][1];
        pz = temp_position[i][2];
    
        node_x = Axx*px + Axy*py + Axz*pz;
        node_y = Ayx*px + Ayy*py + Ayz*pz;
        node_z = Azx*px + Azy*py + Azz*pz;
    
        // Setting position
        if (node_obj.positionX) {
            // node_obj.positionX.value = node_x;
            // node_obj.positionY.value = node_y;
            // node_obj.positionZ.value = node_z;
            // node_obj.positionX.setValueAtTime(node_x, context.currentTime);
            // node_obj.positionY.setValueAtTime(node_y, context.currentTime);
            // node_obj.positionZ.setValueAtTime(node_z, context.currentTime);
            node_obj.positionX.linearRampToValueAtTime(node_x, context.currentTime + 0.1);
            node_obj.positionY.linearRampToValueAtTime(node_y, context.currentTime + 0.1);
            node_obj.positionZ.linearRampToValueAtTime(node_z, context.currentTime + 0.1);
        } else {
            node_obj.setPosition(node_x, node_y, node_z);
        }
        pannerNodesObjects[i] = node_obj;
        if(i==0){
            console.log("Front Left x: " + pannerNodesObjects[i].positionX.value + " y: " + pannerNodesObjects[i].positionY.value  + " z: " + pannerNodesObjects[i].positionZ.value );
        }
        if(i==1){
            console.log("Front Right x: " + pannerNodesObjects[i].positionX.value + " y: " + pannerNodesObjects[i].positionY.value + " z: " + pannerNodesObjects[i].positionZ.value );
        }
        if(i==2){
            console.log("Front Center x: " + pannerNodesObjects[i].positionX.value + " y: " + pannerNodesObjects[i].positionY.value + " z: " + pannerNodesObjects[i].positionZ.value );
        }
        
    }
    
    
    // Starting function creates PannerNodes and adds parameters to them
    function start_function() {
    
        screenCenterY = estimate_ScreenParams();
    
        console.log("Distance from screen in start_function: " + distanceFromScreen);
        console.log("screenCenterY in start_function: " + screenCenterY);
    
        if (listener.forwardX) {
            listener.forwardX.setValueAtTime(0, context.currentTime);
            listener.forwardY.setValueAtTime(0, context.currentTime);
            listener.forwardZ.setValueAtTime(-1, context.currentTime);
            listener.upX.setValueAtTime(0, context.currentTime);
            listener.upY.setValueAtTime(1, context.currentTime);
            listener.upZ.setValueAtTime(0, context.currentTime);
        } else {
            listener.setOrientation(0, 0, -1, 0, 1, 0);
        }
    
        var angleList = [90, -90, 0, -110, 110, -20];
        for (i = 0; i < pannerNodesObjects.length; i++) {
            [nx, ny, nz] = set_rotation(distanceFromScreen, screenCenterY, angleList[i], i);
            set_pannerNode(pannerNodesObjects[i], 'HRTF', "exponential", 1, 100, 2, 360, 0, 0, nx, ny, nz, i);
            splitter.connect(pannerNodesObjects[i], i);
            pannerNodesObjects[i].connect(context.destination);
        }
        console.log(context.destination);
        console.log("start_function Finished!");
    }
    video{ height: 150px }
    <!doctype html>
    <html lang="en">
    <head>
      <meta charset="utf-8">
      <title>MyTitle</title>
      <base href="/">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <link rel="icon" type="image/x-icon" href="favicon.ico">
      <script src="https://unpkg.com/mathjs@7.6.0/dist/math.js"></script>
    </head>
    <body>
      <button type="button" id="start_web_audio" onclick="startFunction()">Click to allow Web Audio</button> <p>Required to start webaudio API due to changes in autoplay policy on modern browsers</p>
        <!-- tell the server we want to read it -->
        <video crossorigin="anonymous" src="https://upload.wikimedia.org/wikipedia/commons/2/22/Volcano_Lava_Sample.webm" controls id="video" loop></video>
    
    </body>
    </html>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-10
      • 2012-02-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多