when i using video source like this (src="demo-video.mp4") its working fine
but when i using link like this (https://api.imovies.app/api/v1/movies/1163/files/512581?source=adjaranet) thumbnail not working. and when i using src like this (./demo-video.mp4) its not working too. can anyone help me to use (https://api.imovies.app/api/v1/movies/1163/files/512581?source=adjaranet) this url of video with Thumbnail?
let preview_video = document.createElement('video')
preview_video.preload = "metadata";
preview_video.width = "500";
preview_video.height = "300"
preview_video.controls = true;
preview_video.src = mainVideo.querySelector("source").src;
preview_video.addEventListener("loadeddata", async function () {
preview_video.pause();
var count = 1;
var id = 1;
var x = 0,
y = 0;
var array = [];
var duration = parseInt(preview_video.duration);
for (var i = 1; i <= duration; i++) {
array.push(i);
}
var canvas;
var i, j;
for (i = 0, j = array.length; i < j; i += horizontalItemCount) {
for (var startIndex of array.slice(i, i + horizontalItemCount)) {
var backgroundPositionX = x * thumbnailWidth;
var backgroundPositionY = y * thumbnailHeight;
var item = thumbnails.find((x) => x.id === id);
if (!item) {
canvas = document.createElement("canvas");
canvas.width = thumbnailWidth * horizontalItemCount;
canvas.height = thumbnailHeight * verticalItemCount;
thumbnails.push({
id: id,
canvas: canvas,
sec: [
{
index: startIndex,
backgroundPositionX: -backgroundPositionX,
backgroundPositionY: -backgroundPositionY,
},
],
});
} else {
canvas = item.canvas;
item.sec.push({
index: startIndex,
backgroundPositionX: -backgroundPositionX,
backgroundPositionY: -backgroundPositionY,
});
}
var context = canvas.getContext("2d");
preview_video.currentTime = startIndex;
await new Promise(function (resolve) {
var event = function () {
context.drawImage(
preview_video,
backgroundPositionX,
backgroundPositionY,
thumbnailWidth,
thumbnailHeight
);
x++;
// removing duplicate events
preview_video.removeEventListener("canplay", event);
resolve();
};
preview_video.addEventListener("canplay", event);
});
// 1 thumbnail is generated completely
count++;
}
// reset x coordinate
x = 0;
// increase y coordinate
y++;
// checking for overflow
if (count > horizontalItemCount * verticalItemCount) {
count = 1;
x = 0;
y = 0;
id++;
}
}
// looping through thumbnail list to update thumbnail
thumbnails.forEach(function (item) {
// converting canvas to blob to get short url
item.canvas.toBlob((blob) => (item.data = URL.createObjectURL(blob)), "image/jpeg");
// deleting unused property
delete item.canvas;
});
});
<video preload="metadata" class="main-video">
<source src="demo-video.mp4" size="480" type="video/mp4">
<source src="How to create Video Playlist Using Html CSS and JavaScript.mp4" size="720" type="video/mp4">
<source src="How to create Video Playlist Using Html CSS and JavaScript.mp4" size="1080" type="video/mp4">
<track label="English" kind="subtitles" src="Titanic.English-WWW.MY-SUBS.CO.srt" srclang="en">
<track label="Urdu" kind="subtitles" src="Titanic.English-WWW.MY-SUBS.CO.srt" srclang="en">
</video>
Related
i am getting frames from gif using Libgif.
and then i am appending those frames in the div with Id = frames.
then i am taking those frames and trying to add each frames one after the other in canvas to make a spritesheet.
in the end i am getting an image in canvas but instead of getting different frames i am getting same image in the spritesheet.
Please help me find the issue.
I had taken canvas width 10000 assuming a gif wont have frames more than 100.
c = document.getElementById("myCanvas");
ctx = c.getContext("2d");
ctx.clearRect(0, 0, ctx.width, ctx.height);
ctx.beginPath();
var imageGiF = "";
var total = 0;
let canvasWidth = 0;
let canvasHeight = 0;
$('div.gifimage img').each(function(idx, img_tag) {
var total = 0;
if (/^.+\.gif$/.test($(img_tag).prop("src"))) {
var rub = new SuperGif({
gif: img_tag,
progressbar_height: 0
});
rub.load(function() {
for (let i = 0; i < rub.get_length(); i++) {
total += 1;
rub.move_to(i);
// var canvas = cloneCanvas(rub.get_canvas());
var canvas = rub.get_canvas().toDataURL("image/png");
img = $('<img id = "gifframe' + i + '"src= "' + canvas + '" class= frameimages>');
$("#frames").append(img);
}
var frameimages = document.getElementById("frames").querySelectorAll(".frameimages");
var totalimages = frameimages.length;
x = 0;
y = 0;
for (let i = 0; i < frameimages.length; i++) {
img = document.getElementById("gifframe" + i + "");
img.onload = function() {
ctx.drawImage(img, i * 100, 0, 100, 100);
total++;
console.log(total);
}
}
totalwidth = (total) * 100;
c.width = totalwidth;
c.height = 100;
setTimeout(() => {
imageGiF = c.toDataURL("image/png");
console.log(imageGiF);
// addBgimg(imageGiF)
}, 10);
});
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdn.rawgit.com/buzzfeed/libgif-js/master/libgif.js"></script>
<div class="gifimage" id="placehere">
<img src="https://media1.giphy.com/media/bzUwzbxcvJ3XQlcnoi/giphy.gif" alt="">
</div>
<div id="frames" class="classGIF"></div>
<canvas id='myCanvas' width="10000" height="300"></canvas>
You were looping through the images, using img in your event handler.
However, this variable img in the outer scope was overridden by every loop, until it was finished looping through everything, then img was stuck on the last frame added.
Then when the event handler triggered, it added the last frame in every instance, because that was the value of img at that point. The loop was done before the images could load.
By adding it to it's own scope by wrapping it in a function, the variable is preserved.
I also modified your code to store the DOM img elements in an array, so you don't need expensive DOM lookups which makes your code a tad bit faster.
I added comments in the code to explain my changes.
c = document.getElementById("myCanvas");
ctx = c.getContext("2d");
ctx.clearRect(0, 0, ctx.width, ctx.height);
ctx.beginPath();
var imageGiF = "";
var total = 0;
let canvasWidth = 0;
let canvasHeight = 0;
$('div.gifimage img').each(function(idx, img_tag) {
var total = 0;
if (/^.+\.gif$/.test($(img_tag).prop("src"))) {
var rub = new SuperGif({
gif: img_tag,
progressbar_height: 0
});
rub.load(function() {
// An array for the image references
let images = [];
// Keep the reference to save on expensive DOM lookups every iteration.
let frames = $("#frames");
for (let i = 0; i < rub.get_length(); i++) {
total += 1;
rub.move_to(i);
// var canvas = cloneCanvas(rub.get_canvas());
var canvas = rub.get_canvas().toDataURL("image/png");
img = $('<img id = "gifframe' + i + '"src= "' + canvas + '" class="frameimages">');
// Use the reference to append the image.
frames.append(img);
// Add image to images array with the current index as the array index.
// Use the jQuery get method to get the actual DOM element.
images[i] = img.get(0);
}
var frameimages = document.getElementById("frames").querySelectorAll(".frameimages");
var totalimages = frameimages.length;
x = 0;
y = 0;
// Loop through all the images in the image array
// Using a scope so the reference to img won't be overridden.
images.forEach((img, index) => {
img.onload = () => {
ctx.drawImage(img, index * 100, 0, 100, 100);
total++;
console.log(total);
}
})
totalwidth = (total) * 100;
c.width = totalwidth;
c.height = 100;
setTimeout(() => {
imageGiF = c.toDataURL("image/png");
console.log(imageGiF);
// addBgimg(imageGiF)
}, 10);
});
}
});
#frames { display:none;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdn.rawgit.com/buzzfeed/libgif-js/master/libgif.js"></script>
<div class="gifimage" id="placehere">
<img src="https://media1.giphy.com/media/bzUwzbxcvJ3XQlcnoi/giphy.gif" alt="">
</div>
<div id="frames" class="classGIF"></div>
<canvas id='myCanvas' width="10000" height="300"></canvas>
Is there a way to capture a higher resolution image than the actual width of my onscreen video element which is showing my webcam image that I intend to capture?
Currently, I have set the width in getUserMedia to 1280, but my element is constrained to 640px. I'd like to still be storing images of 1280px wide so that I'm getting higher quality images.
Code:
<div id="video-container">
<h3 id="webcam-title">Add Photos</h3>
<video id="video" autoplay playsinline></video>
<select id="videoSource"></select>
<div id="take-photo-button" onclick="takeSnapshot();">TAKE PHOTO <div class="overlay"></div></div>
<canvas id="myCanvas" style="display:none;"></canvas>
<div id="snapshot-container"></div>
<div id="approval-form-submit">SAVE ORDER</div>
</div>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script>
$(function() {
var video = document.querySelector('video');
var videoSelect = document.querySelector('select#videoSource');
var initialized = false;
//Obtain media object from any browser
navigator.getUserMedia = ( navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia);
var video_height, snapshot_height;
//var video_width = 1280;
var video_width = 640;
var container_width = 800;
var snapshot_margin = 10;
var snapshot_width = (container_width - snapshot_margin*6)/3;
//var snapshot_width = 1280;
function fillSelectWithDevices(deviceInfos) {
var value = videoSelect.value;
$(videoSelect).empty();
for (let i = 0; i !== deviceInfos.length; ++i) {
var deviceInfo = deviceInfos[i];
if (deviceInfo.kind === 'videoinput') {
var option = document.createElement('option');
option.value = deviceInfo.deviceId;
option.text = deviceInfo.label || `camera ${videoSelect.length + 1}`;
videoSelect.appendChild(option);
if(!initialized && deviceInfo.label==='Back Camera'){
value = deviceInfo.deviceId;
initialized = true;
}
}
if (Array.prototype.slice.call(videoSelect.childNodes).some(n => n.value === value)) {
videoSelect.value = value;
}
}
}
function gotStream(stream) {
window.stream = stream; // make stream available to console
video.srcObject = stream;
video.addEventListener('canplay', function(ev){
video_height = video.videoHeight * (video_width/video.videoWidth);
snapshot_height = video.videoHeight * (snapshot_width/video.videoWidth);
initCanvas();
// Firefox currently has a bug where the height can't be read from
// the video, so we will make assumptions if this happens.
if (isNaN(video_height)) {
video_height = video_width * (3/4);
console.log("Can't read video height. Assuming 4:3 aspect ratio");
}
//video_width=640;
//video_height=480;
video.setAttribute('width', video_width);
video.setAttribute('height', video_height);
canvas.setAttribute('width', video_width);
canvas.setAttribute('height', video_height);
}, false);
return navigator.mediaDevices.enumerateDevices();
}
function handleError(error) {
console.log('navigator.getUserMedia error: ', error);
}
function start() {
if (window.stream) {
window.stream.getTracks().forEach(track => {
track.stop();
});
}
var videoSource = videoSelect.value;
var constraints = {
video: {deviceId: videoSource ? {exact: videoSource} : undefined,
facingMode: "environment",
width:1280},
audio: false
};
navigator.mediaDevices.getUserMedia(constraints).then(gotStream).then(fillSelectWithDevices).catch(handleError);
}
videoSelect.onchange = start;
start();
var canvas, ctx, container;
function initCanvas() {
canvas = document.getElementById("myCanvas");
ctx = canvas.getContext('2d');
container = document.getElementById("snapshot-container");
//Reconstitute snapshots from form URI after failed submit
var image_list_field = $('#image-list-field'),
URI_array = image_list_field.val().split(','),
dataURI;
for(var i = 0;i<URI_array.length;i++){
if(URI_array[i]){
dataURI = "data:image/png;base64,"+URI_array[i];
displaySnapshot(dataURI);
}
}
}
// Capture a photo by fetching the current contents of the video
// and drawing it into a canvas, then converting that to a PNG
// format data URL. By drawing it on an offscreen canvas and then
// drawing that to the screen, we can change its size and/or apply
// other changes before drawing it.
takeSnapshot = function() {
alert (video_width + " " + video_height);
ctx.drawImage(video, 0, 0, video_width, video_height);
var data = canvas.toDataURL('image/png');
displaySnapshot(data);
}
function displaySnapshot(data){
var photo = document.createElement('img'),
snapshot_div = document.createElement('div'),
delete_text = document.createElement('p');
photo.setAttribute('src', data);
$(photo).css({"width":snapshot_width+"px"});
$(photo).addClass("snapshot-img");
$(snapshot_div).css({"width":snapshot_width+"px","height":snapshot_height+25+"px"});
$(delete_text).text("Delete Photo");
$(snapshot_div).append(photo).append(delete_text);
$(delete_text).on('click',function(){$(this).closest('div').remove()})
container.append(snapshot_div);
}
$('#approval-form-submit').on('click',function(e){
var form = $('#approval-form'),
image_list_field = $('#image-list-field'),
imageURI;
image_list_field.val("");
$('.snapshot-img').each(function(i, d){
imageURI = d.src.split(',')[1]+',';
image_list_field.val(image_list_field.val()+imageURI);
});
form.submit();
})
I am currently working on a Javascript project and I am struggling with exporting the entire SVG image on the canvas. So far I've been only able to export the visible part of the canvas, with out the "hidden" parts.
How do I capture the full canvas content?
Is there a way to do it without messing around with the original canvas size?
I am using D3.js V3
Screenshot of my project
Here's my code:
var svgString;
window.onload = function(){
setTimeout(function() {
exportSVG = document.getElementById("canvas");
document.getElementById("canvas").style.fontFamily= "lato";
document.getElementById("canvas").style.width= exportSVG.getBBox().width * 1;
document.getElementById("canvas").style.height= exportSVG.getBBox().height * 1;
svgString = getSVGString(exportSVG);
console.log(exportSVG.getBBox().width + " / " + exportSVG.getBBox().height);
svgString2Image(svgString, exportSVG.getBBox().width, exportSVG.getBBox().height, 'png', save); // passes Blob and filesize String to the callback
console.log("svg export code loaded");
// console.log(svgString.getBBox().width); document.getElementById("canvas").getBBox().width
}, 5000);
};
function save(dataBlob, filesize) {
saveAs(dataBlob, 'D3 vis exported to PNG.png'); // FileSaver.js function
}
// Below are the functions that handle actual exporting:
// getSVGString ( svgNode ) and svgString2Image( svgString, width, height, format, callback )
function getSVGString(svgNode) {
svgNode.setAttribute('xlink', 'http://www.w3.org/1999/xlink');
var cssStyleText = getCSSStyles(svgNode);
appendCSS(cssStyleText, svgNode);
var serializer = new XMLSerializer();
var svgString = serializer.serializeToString(svgNode);
svgString = svgString.replace(/(\w+)?:?xlink=/g, 'xmlns:xlink='); // Fix root xlink without namespace
svgString = svgString.replace(/NS\d+:href/g, 'xlink:href'); // Safari NS namespace fix
return svgString;
function getCSSStyles(parentElement) {
var selectorTextArr = [];
// Add Parent element Id and Classes to the list
selectorTextArr.push('#' + parentElement.id);
for (var c = 0; c < parentElement.classList.length; c++)
if (!contains('.' + parentElement.classList[c], selectorTextArr))
selectorTextArr.push('.' + parentElement.classList[c]);
// Add Children element Ids and Classes to the list
var nodes = parentElement.getElementsByTagName("*");
for (var i = 0; i < nodes.length; i++) {
var id = nodes[i].id;
if (!contains('#' + id, selectorTextArr))
selectorTextArr.push('#' + id);
var classes = nodes[i].classList;
for (var c = 0; c < classes.length; c++)
if (!contains('.' + classes[c], selectorTextArr))
selectorTextArr.push('.' + classes[c]);
}
// Extract CSS Rules
var extractedCSSText = "";
for (var i = 0; i < document.styleSheets.length; i++) {
var s = document.styleSheets[i];
try {
if (!s.cssRules) continue;
} catch (e) {
if (e.name !== 'SecurityError') throw e; // for Firefox
continue;
}
var cssRules = s.cssRules;
for (var r = 0; r < cssRules.length; r++) {
if (contains(cssRules[r].selectorText, selectorTextArr))
extractedCSSText += cssRules[r].cssText;
}
}
return extractedCSSText;
function contains(str, arr) {
return arr.indexOf(str) === -1 ? false : true;
}
}
function appendCSS(cssText, element) {
var styleElement = document.createElement("style");
styleElement.setAttribute("type", "text/css");
styleElement.innerHTML = cssText;
var refNode = element.hasChildNodes() ? element.children[0] : null;
element.insertBefore(styleElement, refNode);
}
}
function svgString2Image(svgString, width, height, format, callback) {
var format = format ? format : 'png';
var imgsrc = 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(svgString))); // Convert SVG string to data URL
var canvas = document.createElement("canvas");
var context = canvas.getContext("2d");
canvas.width = width;
canvas.height = height;
var image = new Image();
image.onload = function() {
context.clearRect(0, 0, width, height);
context.drawImage(image, 0, 0, width, height);
canvas.toBlob(function(blob) {
var filesize = Math.round(blob.length / 1024) + ' KB';
if (callback) callback(blob, filesize);
});
};
image.src = imgsrc;
}
Simply change your <svg> viewBox attribute before you serialize it to a string so that it displays everything:
var svg = document.querySelector('svg');
var toExport = svg.cloneNode(true); // avoids having to reset everything afterward
// grab its inner content BoundingBox
var bb = svg.getBBox();
// update its viewBox so it displays all its inner content
toExport.setAttribute('viewBox', bb.x + ' ' + bb.y + ' ' + bb.width + ' ' + bb.height);
toExport.setAttribute('width', bb.width);
toExport.setAttribute('height', bb.height);
var svgAsStr = new XMLSerializer().serializeToString(toExport);
var blob = new Blob([svgAsStr], {type: 'image/svg+xml'});
var img = new Image();
img.onload = drawToCanvas;
img.src = URL.createObjectURL(blob);
function drawToCanvas(evt) {
var canvas = document.createElement('canvas');
canvas.width = this.width;
canvas.height = this.height;
canvas.getContext('2d').drawImage(this, 0,0);
document.body.appendChild(canvas);
}
svg{border: 1px solid blue}
canvas{border: 1px solid green}
<svg width="50" height="50" viewBox="0 0 50 50">
<rect x="0" y="0" width="200" height="50" fill="#CCC"/>
</svg>
I'm creating audio record app using navigator.mediaDevices.getUserMedia() and it records every sound around me even very quiet and which is 10m away from me. I DO NOT play this sound, I only visualize it depending on volume, so I need only quite loud sounds or which are close to mic, cause there's too much interference.
Also if I enable playback to hear my mic input and start making quiet noise like tapping on the table, I can't here this sound in playback but I see it in visualizer and this is exactly what I don't want
Here's my code:
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
this.audioContext = new AudioContext();
this.sourceNode = this.audioContext.createMediaStreamSource(stream);
this.analyserNode = this.audioContext.createAnalyser();
this.sourceNode.connect(this.analyserNode);
const data = new Float32Array(this.analyserNode.fftSize);
this.analyserNode.getFloatTimeDomainData(data);
So how can I lower mic sensivity using Web Audio API or lower mic input volume or maybe transform data from analyser? I've read about AudioContext.createGain(), gain.volume, but it's used for output audio volume, not input one
I've read about AudioContext.createGain(), gain.volume, but it's used for output audio volume, not input one
No, it is used to control the volume of the audio that goes through it.
You have to see your audio context nodes as a chain, then you might understand that you can indeed use a GainNode to control the input volume of the next node to which it is connected.
Like e.g if we declare something like
gainNode.gain.volume = 0.5;
input.connect(gainNode);
gainNode.connect(analyserNode);
input.connect(audioContext.destination);
it can be seen as
Input [mic] ===> GainNode ===> AnalyserNode
100% || 50% 50%
||
===> AudioContext Output
100%
So your gainNode here did lower the volume of your AnalyserNode, but not the one of the context output.
But this is not really what you want.
Indeed, the AnalyserNode API has minDecibels and maxDecibels properties which will do exactly what you want (filter out out of db range sounds).
But these properties make sense only for frequency data (getXXXFrequencyData) since waveform doesn't take volume into account.
However, it is still possible to check if this frequency data is in our required bounds before deciding if we should draw our waveform or not.
polyfill();
(async() => {
const ctx = new AudioContext();
const input = await loadFileAsBufferNode(ctx);
const analyser = ctx.createAnalyser();
analyser.minDecibels = -90;
analyser.maxDecibels = -10;
analyser.fftSize = 512;
input.connect(analyser);
const gainNode = ctx.createGain();
input.connect(gainNode);
const bufferLength = analyser.frequencyBinCount;
const freqArray = new Uint8Array(bufferLength);
const waveArray = new Uint8Array(bufferLength);
const canvasCtx = canvas.getContext('2d');
const WIDTH = canvas.width;
const HEIGHT = canvas.height;
canvasCtx.lineWidth = 2;
draw();
// taken from https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode/maxDecibels#Example
function draw() {
requestAnimationFrame(draw);
canvasCtx.clearRect(0, 0, WIDTH, HEIGHT);
analyser.getByteFrequencyData(freqArray);
gainNode.gain.value = 1;
analyser.getByteTimeDomainData(waveArray);
var barWidth = (WIDTH / bufferLength) * 2.5;
var barHeight;
var x = 0;
for (var i = 0; i < bufferLength; i++) {
barHeight = freqArray[i];
canvasCtx.fillStyle = 'rgb(' + (barHeight + 100) + ',50,50)';
canvasCtx.fillRect(x, HEIGHT - barHeight / 2, barWidth, barHeight / 2);
x += barWidth + 1;
}
// here we check if the volume is in bounds
if (freqArray.some(isTooHigh) || !freqArray.some(hasValue)) {
canvasCtx.fillRect(0, HEIGHT / 2, WIDTH, 1);
gainNode.gain.value = 0;
return;
}
canvasCtx.beginPath();
var sliceWidth = WIDTH * 1.0 / bufferLength;
var x = 0;
for (var i = 0; i < bufferLength; i++) {
var v = waveArray[i] / 128.0;
var y = v * HEIGHT / 2;
if (i === 0) {
canvasCtx.moveTo(x, y);
} else {
canvasCtx.lineTo(x, y);
}
x += sliceWidth;
}
canvasCtx.lineTo(canvas.width, canvas.height / 2);
canvasCtx.stroke();
};
function isTooHigh(val) {
return val === 255;
}
function hasValue(val) {
return val;
}
// DOM
maxDB.oninput = e => {
const max = +maxDB.value;
if (+minDB.value >= max) minDB.value = analyser.minDecibels = max - 1;
analyser.maxDecibels = max;
}
minDB.oninput = e => {
const min = +minDB.value;
if (+maxDB.value <= min) maxDB.value = analyser.maxDecibels = min + 1;
analyser.minDecibels = min;
}
out.onchange = e => {
if (out.checked)
gainNode.connect(ctx.destination);
else
gainNode.disconnect(ctx.destination);
};
})();
function loadFileAsBufferNode(ctx, url = 'https://dl.dropboxusercontent.com/s/8c9m92u1euqnkaz/GershwinWhiteman-RhapsodyInBluePart1.mp3') {
return fetch(url)
.then(r => r.arrayBuffer())
.then(buf => ctx.decodeAudioData(buf))
.then(bufferNode => {
const source = ctx.createBufferSource();
source.buffer = bufferNode;
source.repeat = true;
source.start(0);
return source;
});
};
/* for Safari */
function polyfill() {
window.AudioContext = window.AudioContext || window.webkitAudioContext;
try {
const prom = new AudioContext().decodeAudioData(new ArrayBuffer()).catch(e => {});
} catch (e) {
const prev = AudioContext.prototype.decodeAudioData;
Object.defineProperty(AudioContext.prototype, 'decodeAudioData', {
get: () => asPromise
});
function asPromise(audioBuffer, done, failed) {
return new Promise((res, rej) => {
prev.apply(this, [audioBuffer, onsuccess, onerror]);
function onsuccess(buf) {
if (typeof done === 'function') done(buf);
res(buf);
}
function onerror(err) {
if (typeof failed === 'function') failed(err);
rej(err);
}
});
}
}
}
<label>min<input type="range" id="minDB" min="-100" max="-1" value="-90"></label>
<label>max<input type="range" id="maxDB" min="-99" max="0" value="-10"></label>
<label>output audio<input type="checkbox" id="out"></label>
<canvas id="canvas"></canvas>
I'm creating a audio player with visualizer.
But currently when I press the input to start the audio player my debug console returns:
Uncaught (in promise) DOMException: Failed to load because no
supported source was found.
What I'm currently doing is setting the whole audio element up in JS / jQuery:
var bins = 512;
var backgroundColour = "#2C2E3B";
var barColour = "#EC1A55";
var floorLevel = 32;
var audioContext;
var audioBuffer;
var audioAnalyserNode;
var initialized = false;
var songText = "";
var textSize;
var freqLookup = [];
var canvasContext;
var isStream = true;
var canvasWidth;
var canvasHeight;
var src;
var audioElement;
var isPlaying = false;
var volume = 1;
function play() {
audioElement = document.createElement('audio');
// Opus support check stuff
var streamEndpoint = 'http://**.**.**.**:8003/stream';
var canPlayOpus = (typeof audioElement.canPlayType === "function" && audioElement.canPlayType('audio/ogg; codecs="opus"') !== "");
if(volume > 1) {
volume = volume / 100;
}
audioElement.src = streamEndpoint;
audioElement.crossOrigin = 'anonymous';
audioElement.volume = volume;
audioElement.play();
isPlaying = true;
setUpCanvas(audioElement);
}
function pause() {
audioElement.pause();
audioElement.currentTime = 0;
audioElement.src = '';
isPlaying = false;
}
function setUpCanvas(audioElement){
try {
initCanvas(document.getElementById("canvas"));
if(typeof audioContext === 'undefined') {
audioContext = new AudioContext();
}
if (audioElement) {
isStream = true;
setupAudioApi(true, audioElement);
}
} catch(e) {
console.log(e);
}
}
function setupAudioApi(isStream, audioElement) {
//var src;
if (isStream){
if(typeof src === 'undefined'){
src = audioContext.createMediaElementSource(audioElement);
audioContext.crossOrigin = "anonymous";
audioAnalyserNode = audioContext.createAnalyser();
audioAnalyserNode.fftSize = bins * 4;
src.connect(audioAnalyserNode);
audioAnalyserNode.connect(audioContext.destination);
}
}
if (!isStream) {
src.start();
}
initialized = true;
initFreqLookupTable();
}
function initCanvas(canvasElement) {
canvasContext = canvasElement.getContext('2d');
canvasElement.width = canvasElement.clientWidth;
canvasElement.height = canvasElement.clientHeight;
canvasWidth = canvasElement.width;
canvasHeight = canvasElement.height;
requestAnimationFrame(paint);
}
function getFreqPoint(start, stop, n, binCount) {
return start * Math.pow(stop / start, n / (binCount - 1));
}
function initFreqLookupTable() {
var lastPoint = 0;
var bins = audioAnalyserNode.frequencyBinCount;
for(var i = 0; i < bins / 2; i++) {
//Scale to perceived frequency distribution
var newFreq = getFreqPoint(20, 20000, i * 2, bins);
var point = Math.floor(bins * newFreq / 20000);
while (point <= lastPoint) {
point++;
}
lastPoint = point;
freqLookup.push(point);
}
}
//Render some fancy bars
function paint() {
requestAnimationFrame(paint);
if(!initialized) {
alert('Er is iets fout gegaan');
return false;
}
canvasContext.clearRect(0, 0, canvasWidth, canvasHeight);
canvasContext.fillStyle = backgroundColour;
canvasContext.fillRect(0, 0, canvasWidth, canvasHeight);
var bins = audioAnalyserNode.frequencyBinCount;
var data = new Uint8Array(bins);
audioAnalyserNode.getByteFrequencyData(data);
canvasContext.fillStyle = barColour;
for(var i = 0; i < bins; i++) {
var point = freqLookup[i];
//Pretty much any volume will push it over 128 so we set that as the bottom threshold
//I suspect I should be doing a logarithmic space for the volume as well
var height = Math.max(0, (data[point] - floorLevel));
//Scale to the height of the bar
//Since we change the base level in the previous operations, 256 should be changed to 160 (i think) if we want it to go all the way to the top
height = (height / (256 - floorLevel)) * canvasHeight * 0.8;
var width = Math.ceil(canvasWidth / ((bins / 2) - 1));
canvasContext.fillRect(i * width, canvasHeight - height, width, height);
}
}
The stream is in audio/mpeg format, it does load when I simply create an audio element in HTML with a src.
Can someone help me clarify and find the solution to the DOMException I'm getting. I have been searching other cases of this error but the fixes there didn't resolve the problem.
Try creating the audio tag like this:
var audio = new Audio('audio_file.mp3');
And try setting the type:
audio.type = "audio/mpeg";
I think that will fix your problem.
This creates an element, identical to the one you use in your code.
I suggest you put an extension on your stream.
I know this way works, and I don't know why the other way doesn't.