我正在尝试从视频中获取音频以使用Web audio API。但视频中的音频是静音的。当我在本地测试这个代码时,HTML5音频是有效的(在jsfiddle上,当Web audio API打开时,它不起作用(,但在本地和jsfiddlevideo上没有音频(它被静音,用户无法更改(。控制台中没有显示任何错误。我添加了应用于自动播放策略更改的功能: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>
通常情况下,当创建MediaElementSource(MES(时,其目标MediaElement将将其音频流路由到MES,如果该MES未连接到AudioContext的目的地,则MediaSource的音频将静音。
但这里不是这样
在这里,您的问题是该文件没有按照同源策略提供,因此您的代码不允许读取该媒体的内容,并且在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>