maybe i just got it wrong but... im requesting "large" files via ajax (180mb - 500mb). i thought that im able to fetch and use the data with the method URL.createObjectURL while its actually loading? i need the requested data within 5 seconds but its acutually loading 16 seconds.
ajax request
xhr.onload (worked within 5 seconds or faster, locally, but not live)
within the onload (or progress, onreadystatechange (i tried)) i used URL.createObjectURL(xhr.response) to get the data
var nxtclp = new XMLHttpRequest();
nxtclp.onload = function() {
get_src = URL.createObjectURL(nxtclp.response);
that.preloadSource = get_src;
};
nxtclp.open("GET", "media/vid.mp4");
nxtclp.responseType = "blob";
nxtclp.send();
is there any way to playback data while loading ?
Use autoplay attribute at <video> element
autoplay
A Boolean attribute; if specified, the video automatically begins to
play back as soon as it can do so without stopping to finish loading
the data.
<video controls autoplay src="http://mirrors.creativecommons.org/movingimages/webm/ScienceCommonsJesseDylan_240p.webm">
</video>
alternatively, using javascript
var video = document.createElement("video");
video.autoplay = true;
video.controls = true;
video.onloadedmetadata = (e) => {
console.log(video.readyState);
document.body.appendChild(e.path[0])
}
video.src = "http://mirrors.creativecommons.org/movingimages/webm/ScienceCommonsJesseDylan_240p.webm";
<body>
</body>
Related
I want to dynamically load videos from a server to a client using javascript DOM manipulation. I tried this code but the callback function onload doesn't run and in addition in the network tab in the request headers accept field is "/".
function doAjaxVideo(param, lambda) {
let video = document.createElement("video");
let sourceElem = document.createElement("source");
sourceElem.src = param;
sourceElem.type = "video/webm";
video.appendChild(sourceElem);
video.autoplay = "true";
video.onload = () => lambda();
}
doAjaxVideo("/video.webm", function() { console.log("Ready!") }
Any suggestions on how to tweak this so it works? Or maybe another way of doing it.
Image of network tab in devtools
You can try onloadeddata. https://www.w3schools.com/jsref/event_onloadeddata.asp
Because you want it to take action when the data is loaded, not the video element itself.
I would to load a BLOB video as Youtube or Netflix for example.
That is, load the video from 0s to 10s in BLOB, display it and then load the video from 10s to 20s.
I thought about doing this code (which works without the #t =0,10 but waiting for the entire video to load before returning it).
var query = new XMLHttpRequest();
var videolink = "test.mp4";
var videobalise = $("video");
var get = videolink + "#t=0,10";
query.open("GET", get, true);
query.responseType = "blob";
query.onload = function(){
if(this.status === 200){
var currentTime = videobalise[0].currentTime;
var paused = false;
if(!videobalise[0].paused) paused = true;
window.URL = window.URL || window.webkitURL;
var videoBlob = this.response;
var video = window.URL.createObjectURL(videoBlob);
videobalise.attr("src", video);
videobalise[0].currentTime = currentTime;
if(paused == true){
videobalise[0].play();
}
}
}
query.send();
Thanks in advance,
Thomas
I can't tell for Netflix, but for YouTube, that's not what they do.
The blob URI you see as the src of their <video> element points to a MediaSource object, not to a Blob.
Now, the #t fragment identifier in a MediaElement src works only for MediaElements.
MediaElements will try to load only the required data from the server. This is done thanks to Range requests. But it requires that the media's metadata has been already fetched and parsed, so that the browsers can know at which byte-range the next data to load is located.
The #t fragment identifier is just a way to tell the browser that we are only interested in the range being defined in there, and thus, this fragment identifier also needs that the media's metadata are accessible, and provide the correct bytes offset.
All this to tell that you actually need the full file to be available at the URI on which you set this #t fragment identifier.
So in your case, you would have to load the full file from the original URI, and only on the blob URI that you'll set as the <video>'s src would you append the fragment identifier:
var url = "https://upload.wikimedia.org/wikipedia/commons/transcoded/a/a4/BBH_gravitational_lensing_of_gw150914.webm/BBH_gravitational_lensing_of_gw150914.webm.480p.webm";
fetch(url) // fetch the whole file
.then(resp => resp.blob())
.then(blob => {
const blobURI = URL.createObjectURL(blob);
const fragId = '#t=5' // starts at 5s
vid.src = blobURI + fragId; // here you set the fragId
});
video{height:100vh}
<video id="vid" controls></video>
So this is probably not what you want, since you would actually load the full file.
But I guess that what you really wanted was a way to hide the original file, in a desperate attempt to block your users from downloading it. Then, I am sorry to tell you that this won't work at all, because your AJAX request is clearly visible.
Note that even fetching yourself by range and using a MediaSource like YT won't work either, YT videos are actually downloadable.
The best move if you really want to do that might be to use the Encrypted Media Extensions API.
I have a simple xmlhttprequest running for fetching an audio file, when it's done fetching it decodes the audio and plays it.
var xhr = new XMLHttpRequest();
xhr.open('GET', /some url/, true);
xhr.responseType = 'arraybuffer';
xhr.onload = function() {
decode(xhr.response);
}.bind(this);
xhr.send(null);
The problem with this however, is that the file decodes only after the request is loaded/finished downloading. Is there an approach for streaming audio without having to wait for it to finish downloading?, without the usage of <audio> tags
You still need HTML5 Audio object but instead of using it directly and playing with it you can use use MediaElementAudioSourceNode along with Audio element to take advantage of Web API.
Excerpt from here
Rather than going the usual path of loading a sound directly by
issuing an XMLHttpRe quest and then decoding the buffer, you can use
the media stream audio source node (MediaElementAudioSourceNode) to
create nodes that behave much like audio source nodes
(AudioSourceNode), but wrap an existing tag. Once we have this
node connected to our audio graph, we can use our knowledge of the Web
Audio API to do great things. This small example applies a low-pass
filter to the tag:
Sample Code:
window.addEventListener('load', onLoad, false);
function onLoad() {
var audio = new Audio();
source = context.createMediaElementSource(audio);
var filter = context.createBiquadFilter();
filter.type = filter.LOWPASS;
filter.frequency.value = 440;
source.connect(this.filter);
filter.connect(context.destination);
audio.src = 'http://example.com/the.mp3';
audio.play();
}
HTML
<textarea id="words" name="words"></textarea>
<audio id="type" src="type.mp3"></audio>
JS
document.getElementById('words').onkeydown = function(){
document.getElementById('type').play();
}
I want to make type.mp3 to play anytime I press any key.
But, it is not played in sync with the key.
I am looking for a pure JS solution.
The audio media element depends on the buffering mechanism of the browser and may not play instantly when play is called.
To play sounds in sync with key-presses you would have to use the Web Audio API instead which allows you to play a in-memory buffer and therefor instantly.
Here is an example of how you can load and trigger the sound:
window.AudioContext = window.AudioContext || window.webkitAudioContext;
var request = new XMLHttpRequest(),
url = "https://dl.dropboxusercontent.com/s/8fp1hnkwp215gfs/chirp.wav",
actx = new AudioContext(),
abuffer;
// load file via XHR
request.open("GET", url, true);
request.responseType = "arraybuffer";
request.onload = function() {
// Asynchronously decode the audio file data in request.response
actx.decodeAudioData(request.response,
function(buffer) {
if (buffer) {
abuffer = buffer; // keep a reference to decoded buffer
setup(); // setup handler
}
}
)
};
request.send();
// setup key handler
function setup() {
document.getElementById("txt").onkeydown = play;
}
// play sample - a new buffer source must be created each time
function play() {
var src = actx.createBufferSource();
src.buffer = abuffer;
src.connect(actx.destination);
src.start(0);
}
<textarea id=txt></textarea>
(note: there seem to be a bug in Firefox at the time of this writing reporting a column which does not exist in the code at the send() call - if a problem, try the code in Chrome).
Javascript is an asynchronous and event-driven language, so you can't make a synchronous function.
I have a high quality video which I cannot compress too much as it's going to be the base of a lot of image analysis whereby each frame will be redrawn into the canvas and then manipulated.
I'm trying to preload the whole thing before playing it as I can't have the video stop, buffer and continue. Is there an event which I can listen for which signifies that the whole video has preloaded before I commence playback?
Here's how I'm doing it in JS/jQuery:
this.canvas = this.el.find("canvas")[0];
this.video = this.el.find("video")[0];
this.ctx = this.canvas.getContext("2d");
this.video.autoplay = false;
this.video.addEventListener("play",this.draw)
this.video.addEventListener("timeupdate",this.draw)
this.video.addeventlistener("ended",this.trigger("complete",this))
This will load the entire video in JavaScript
var r = new XMLHttpRequest();
r.onload = function() {
myVid.src = URL.createObjectURL(r.response);
myVid.play();
};
if (myVid.canPlayType('video/mp4;codecs="avc1.42E01E, mp4a.40.2"')) {
r.open("GET", "slide.mp4");
}
else {
r.open("GET", "slide.webm");
}
r.responseType = "blob";
r.send();
canplaythrough is the event that should fire when enough data has downloaded to play without buffering.
From the Opera teams excellent (although maybe very slightly dated now) resource Everything you need to know about HTML5 video and audio
If the load is successful, whether using the src attribute or using source elements, then as data is being downloaded, progress events are fired. When enough data has been loaded to determine the video's dimensions and duration, a loadedmetadata event is fired. When enough data has been loaded to render a frame, the loadeddata event is fired. When enugh data has been loaded to be able to play a little bit of the video, a canplay event is fired. When the browser determines that it can play through the whole video without stopping for downloading more data, a canplaythrough event is fired; this is also when the video starts playing if it has a autoplay attribute.
'canplaythrough' support matrix available here: https://caniuse.com/mdn-api_htmlmediaelement_canplaythrough_event
You can get around the support limitations by binding the load element to the same function, as it will trigger on those.
Download the video using fetch
Convert the response to a blob
Create an object URL from the blob (e.g. blob:http://localhost:8080/df3c4336-2d9f-4ba9-9714-2e9e6b2b8888)
async function preloadVideo(src) {
const res = await fetch(src);
const blob = await res.blob();
return URL.createObjectURL(blob);
}
Usage:
const video = document.createElement("video");
video.src = await preloadVideo("https://example.com/video.mp4");
Hope this could help you
var xhrReq = new XMLHttpRequest();
xhrReq.open('GET', 'yourVideoSrc', true);
xhrReq.responseType = 'blob';
xhrReq.onload = function() {
if (this.status === 200) {
var vid = URL.createObjectURL(this.response);
video.src = vid;
}
}
xhrReq.onerror = function() {
console.log('err' ,arguments);
}
xhrReq.onprogress = function(e){
if(e.lengthComputable) {
var percentComplete = ((e.loaded/e.total)*100|0) + '%';
console.log('progress: ', percentComplete);
}
}
xhrReq.send();
and then , if your video src has another domain ,you have to handle CORS .
So far the most trustable solution we found was to play it and wait for the buffer to be fully loaded.
Which means if the video is long, you will have to wait for almost all the video length.
That isn't cool, i know.
Wondering if someone has figured out some other magically reliable way of doing it ( ideally using something like PreloadJS which automatically falls back to flash when HTML5 video isn't supported ).
You can use this nice plugin:
https://github.com/GianlucaGuarini/jquery.html5loader
In its API there is a onComplete event that is triggered when the plugin finishes to load all the sources
Does this work?
video.onloadeddata = function(){
video.onseeked = function(){
if(video.seekable.end(0) >= video.duration-0.1){
alert("Video is all loaded!");
} else {
video.currentTime=video.buffered.end(0); // Seek ahead to force more buffering
}
};
video.currentTime=0; // first seek to trigger the event
};