I've built a simple audio player for my website using HTMLMediaElement (I'm avoiding the <audio> element due to personal reasons)
Everything seems to be working fine, however when I want to replace the source using replaceAudio(), I get this error:
Uncaught IndexSizeError: Failed to execute 'end' on 'TimeRanges': The index provided (0) is greater than or equal to the maximum bound (0).
Here's my code:
// ********************************************************************** //
// ****************************** Audio.js ****************************** //
// ********************************************************************** //
// Declare element and event listeners
audio = new Audio()
function replaceAudio() {
audio.pause()
audio.currentTime = 1;
audio = new Audio('/static/users/' + trackUser + '/tracks/' + trackSlug + '.mp3');
audio.load()
}
function addAudioEventListeners() {
audio.addEventListener('timeupdate', updateProperties, false)
audio.addEventListener('ended', audioEnded, false)
}
// *** Declare minor functions
// Sets the progress or buffer width
...
// Converts the time integers to formatted time
...
// Get properties and update them
function updateProperties() {
...
function getAudioProps() {
...
buffered = audio.buffered
// Return the values
return {time:time,dur:dur}
}
var getAudioProps = getAudioProps();
...
var bufferend = buffered.end(0) / dur
...
setAudioIndicatorWidth(bufferend, bufferElement);
}
function audioEnded() {}
Before start calling buffered.end(0) try check buffered.length first
if(buffered.length>0)
// call buffered.end(0) / buffered.start(0)
Related
I try to get the buffered parts of media element (specifically I try to get it from a video element, but I want it to be able to use audio too), but when I use the start() or end() functions with some offset (for example, 0), The log returns the following error:
IndexSizeError: Index or size is negative or greater than the allowed amount
What's wrong with my code?
var mediaelement = function(e) {
return e.buffered.start(0);
}
console.log(mediaelement(document.querySelector('video')));
Most probably, there is nothing buffered yet when you call your function.
You should add a check to e.buffered.length before calling TimeRanges.start() or TimeRanges.end():
function getbufferedstart(el) {
if(el.buffered.length) {
return el.buffered.start(0);
}
else {
return 'avoided a throw';
}
}
var a = new Audio('https://dl.dropboxusercontent.com/s/8c9m92u1euqnkaz/GershwinWhiteman-RhapsodyInBluePart1.mp3');
a.onloadedmetadata = onwehavebufferedsomething;
console.log(getbufferedstart(a)); // avoided
console.log('really ?');
a.buffered.start(0); // Error
function onwehavebufferedsomething(evt) {
console.log("now it's ok");
console.log(getbufferedstart(a)); // 0
}
body>.as-console-wrapper{max-height:100vh}
I am trying to stream mp3 data from my server to the client side. I am doing this using Ajax. The server sends 50 kilobytes per request. I wrote two functions: one that gets the mp3 data and one that plays them. The first function takes the 50 kilobytes, decodes them and stores the decoded data in an array then it calls itself recursively. The second function starts playing as soon as the first element in the array is filled with data. The problem is that this works for the first 50kilobytes only then it fails. What I want to do is keep my get_buffer function running until the server tells it no more data to send, and keep my play() function playing data until there is no more elements in the array.
Here is my two functions:
function buffer_seg() {
// starts a new request
buff_req = new XMLHttpRequest();
// Request attributes
var method = 'GET';
var url = '/buffer.php?request=buffer&offset=' + offset;
var async = true;
// set attributes
buff_req.open(method, url, async);
buff_req.responseType = 'arraybuffer';
// keeps loading until something is recieved
if (!loaded) {
change_icon();
buffering = true;
}
buff_req.onload = function() {
segment = buff_req.response;
// if the whole file was already buffered
if (segment.byteLength == 4) {
return true;
} else if (segment.byteLength == 3) {
return false;
}
// sets the new offset
if (offset == -1) {
offset = BUFFER_SIZE;
} else {
offset += BUFFER_SIZE;
}
//decodes mp3 data and adds it to the array
audioContext.decodeAudioData(segment, function(decoded) {
buffer.push(decoded);
debugger;
if (index == 0) {
play();
}
});
}
buff_req.send();
buff_seg();
}
Second function:
function play() {
// checks if the end of buffer has been reached
if (index == buffer.length) {
loaded = false;
change_icon();
if (buffer_seg == false) {
stop();
change_icon();
return false;
}
}
loaded = true;
change_icon();
// new buffer source
var src = audioContext.createBufferSource();
src.buffer = buffer[index++];
// connects
src.connect(audioContext.destination);
src.start(time);
time += src.buffer.duration;
src.onended = function() {
src.disconnect(audioContext.destination);
play();
}
}
The recursive call to buffer_seg is in the main body of buffer_seg, not in the callback, so it happens immediately - not, as you seem to intend, after a response is received. Second, this also means that the recursive call is unconditional when it should be based on whether the previous response indicated more data would be available. If this isn't just crashing your browser, I'm not sure why. It also means that chunks of streamed audio could be pushed into the buffer out of order.
So to start I'd look at moving the recursive call to the end of the onload handler, after the check for end of stream.
In the 2nd function, what do you intend if (buffer_seg == false) to do? This condition will never be met. Are you thinking this is a way to see the last return value from buffer_seg? That's not how it works. Perhaps you should have a variable that both functions can see, which buffer_seg can set and play can test, or something like that.
So hello community.
I develop a website generating large files via PHP, which can take up to 5 minutes. I raised the settings of PHP value for max execution time, no problem here.
When the user calls for the generated file, a loader is shown on the current page, this loader executes a looped Ajax request, reading a simple text file, containing the status of the file generation.
Ex: «Generating line 120/350».
This works fine, each time te ajax request provides a result, it calls itself again after 2.5 seconds. Until more or less 1 minute 30, then the log freezes. And i can't figure out what happens. This is what i got so far :
There is no Apache crash, nor error in the log
When i try to access my text file, after that freezing, via the browser, this latter keeps loading, without displaying. Until the file-generating script has finished. Then it displays my text file sayin "All done".
Now the interesting part : if during this process, i access my text file with an other browser, the files is displayed just fine and every update of it in the borwser show the evolution of the log running! That is what bugs me most.
So, server side, my text file IS generated and updated, until the end of the logging process (it updates correctly during all the file creation process). Just, accessing my text file is not possible with the same browser.
This happens both locally and online.
Who can tell whe what happens / how to solve this issue?
Jquery plugin (hand made) :
;jQuery(function ($) {
// dynlog object
$.fn.dynlog = function(options){
// user id MUST be defined
if(typeof(uid) == "undefined"){
console.log("Function dynlog: Missing current user ID!");
return;
}
uid = parseInt( uid );
// "$D" is current instance of the object
var $D = this;
// plugin's default options
var defaults = {
// frequency in milliseconds between two ajax checks of the log value
// raise for optimisation, lower for user experience
frequency: 1500
, default_text: 'Chargement'
// Callbacks
, onStart: function() { return true; }
, onStop: function(inst) { return true; }
, onInit: function(inst) { return true; }
}
// mergin defaults, and given params
$D.settings = $.extend({}, defaults, options);
// LOCAL VARS
var running = false
, runningClass = false
, return_txt
, current_txt
, client = new XMLHttpRequest() // For reading file
, to // Timeout, with throttle
;
// /////////////////////
// METHODS:
// /////////////////
// init
$D.init = function(inst) {
// external function
$D.settings.onInit();
}
// /////////////////
// Démarrer l'affichage du log
$D.start = function(){
running = true;
// Adding class 'running' to container
$D.find('.dynlog-container').addClass('running');
// Let's go get the log value
$D.fetch();
}
// /////////////////
// Go seek log value
$D.fetch = function(){
// read log text file => see further fo "on-read" event
client.open('GET', 'files/dynlog/'+uid+'.txt');
client.send(null);
}
// /////////////////
// Arréter l'affichage du log
$D.stop = function(){
// do not update anymore, please
running = false;
// removes running status fos visual update
$D.find('.dynlog-container').removeClass('running');
runningClass = false;
}
// /////////////////////
// PRIVATE METHODS
// /////////////////////
// /////////////////
// Update log visually
var update = function(text){
// Afficher le texte donné
$D.find('.dynlog-container .dynlog-message').text( text );
};
// /////////////////////
// ONCE FILE IS SEEKED
// /////////////////////
// /////////////////
// What happens when file seeked ?
client.onreadystatechange = function(){
// Text to display in log
return_txt = false;
// If file is found
if(client.status == 200){
// its content is the text to display
return_txt = client.responseText;
}
// If file NOT found or text is defined but null
if(client.status == 404 || return_txt === ''){
// please write the default value
return_txt = $D.settings.default_text;
}
// if text is retuned AND different from previous
if(return_txt != '' && return_txt != current_txt){
// then please update it
current_txt = return_txt;
update( current_txt );
}
// cycle the call if we are still running
if(client.status != 0 && running){
// throttler :
clearTimeout(to);
// re-call the start stuff
to = setTimeout(function(){
// $D.start();
$D.fetch();
}, $D.settings.frequency);
}
};
// /////////////////////
// Applying plugin to jquery object
$(this).each(function(){
// création du template
var tpl = '<div class="dynlog-container">'
+'<div class="dynlog-message"></div>'
+'</div>'
;
$(this).append(tpl);
});
// /////////////////////
// call the "constructor" method
$D.init(this);
// /////////////////////
// returns this instance
return this;
}
});
PHP script basically does:
file_put_contents( $file, $contents);
My Chrome app has a function that asks for a file to be loaded by another function, checks that the function has set a flag signifying success (External.curFile.lodd), then attempts to process it. My problem is that the flags are not set the first time I call the function, but when I call it a second time the flags are already set.
I had a feeling this has to do with Chrome file functions being asynchronous, so I had the first function idle for a bit while the file loads. The first load never succeeds, no matter how long I wait, but the second load always does!
Calling Function:
function load_by_lines_from_cur_dir( fileName, context ){ // determine the 'meaning' of a file line by line, return last 'meaning', otherwise 'null'
var curLineMeaning = null;
var lastLineValid = true;
External.read_file_in_load_path(fileName); // 'External' load 'fileName' and reads lines, REPLacement does not see this file
// This is a dirty workaround that accounts for the fact that 'DirectoryEntry.getFile' is asynchronous, thus pre-parsing checks fail intil loaded
var counter = 0, maxLoops = 10;
nuClock();
do{
sleep(500);
counter++;
preDebug.innerText += '\r\nLoop:' + counter + " , " + time_since_last();
}while( !External.curFile.lodd && (counter < maxLoops) ); //idle and check if file loaded, 5000ms max
preDebug.innerText += '\r\nLoaded?:' + External.curFile.lodd;
preDebug.innerText += '\r\nLines?:' + External.curFile.lins;
if( External.curFile.lodd ){ // The last load operating was successful, attempt to parse and interpret each line
// parse and interpret lines, storing each meaning in 'curLineMeaning', until last line is reached
while(!External.curFile.rEOF){
curLineMeaning = meaning( s( External.readln_from_current_file() ), context);
preDebug.innerText += '\r\nNext Line?: ' + External.curFile.lnnm;
preDebug.innerText += '\r\nEOF?: ' + External.curFile.rEOF;
}
} // else, return 'null'
return curLineMeaning; // return the result of the last form
}
which calls the following:
External.read_file_in_load_path = function(nameStr){ // Read the lines of 'nameStr' into 'External.curFile.lins'
External.curPath.objt.getFile( // call 'DirectoryEntry.getFile' to fetch a file in that directory
nameStr,
{create: false},
function(fileEntry){ // action to perform on the fetched file, success
External.curFile.name = nameStr; // store the file name for later use
External.curFile.objt = fileEntry; // store the 'FileEntry' for later use
External.curFile.objt.file( function(file){ // Returns 'File' object associated with selected file. Use this to read the file's content.
var reader = new FileReader();
reader.onload = function(e){
External.curFile.lodd = true; // File load success
};
reader.onloadend = function(e){
//var contents = e.target.result;
// URL, split string into lines: http://stackoverflow.com/questions/12371970/read-text-file-using-filereader
External.curFile.lins = e.target.result.split('\n'); // split the string result into individual lines
};
reader.readAsText(file);
External.curFile.lnnm = 0; // Set current line to 0 for the newly-loaded file
External.curFile.rEOF = false; // Reset EOF flag
// let's try a message instead of a flag ...
/*chrome.runtime.sendMessage({greeting: "hello"}, function(response) {
console.log(response.farewell);
});*/
} );
},
function(e){ External.curFile.lodd = false; } // There was an error
);
};
This app is a dialect of Scheme. It's important that the app knows that the source file has been loaded or not.
I didn't read through all of your code, but you can't kick off an asynchronous activity and then busy-wait for it to complete, because JavaScript is single threaded. No matter what's happened, the asynchronous function won't be executed until the script completes its current processing. In other words, asynchronous does not imply concurrent.
Generally speaking, if task A is to be performed after asynchronous task B completes, you should execute A from the completion callback for B. That's the straightforward, safe way to do it. Any shortcut, to achieve better responsiveness or to simplify the code, is going to have dependency or race-condition problems, and will require lots of horsing around to get right. Even then, it will be hard to prove that the code operates correctly on all platforms in all circumstances.
Boy-oh-boy do I hate external interface. I have a video player that utilizes external interface to control the flash object and to allow the flash object to pass messages to the same javascript. For a time it worked well in all browsers. Then a few days ago i went to go test it in all browsers before i moved the project out of development, and found that the application broke in internet explorer 9. The following error appeared in the console:
SCRIPT16389: Could not complete the operation due to error 8070000c.
jquery.min.js, line 16 character 29366
My javascript file is really long but here are the important parts. All my actions are contained in an object that i created. Inside one of my methods i have the following lines:
var that = this;
that.stop();
here are all the methods that get called as a result of that method:
this.stop = function(){
var that = this;
console.log('stop called');
that.pause();
that.seek(0);
that.isPlaying = false;
console.log('stop finished');
};
this.pause = function(){
var that = this;
console.log('pause called');
if(that.player == 'undefined' || that.player == null){
that.player = that.GetMediaObject(that.playerID);
}
that.player.pauseMedia(); //external interface call
that.isPlaying = false;
console.log('pause finished');
};
this.seek = function(seek){
var that = this;
console.log('seek called');
if(that.player == 'undefined' || that.player ==null){
console.log("player="+that.player+". resetting player object");
that.player = that.GetMediaObject(that.playerID);
console.log("player="+that.player);
}
that.player.scrubMedia(seek); //external interface call
console.log('seek finished');
};
//this method returns a reference to my player. This method is call once when the page loads and then again as necessary by all methods that make external interface calls
this.GetMediaObject = function(playerID){
var mediaObj = swfobject.getObjectById(playerID);
console.log('fetching media object: ' +mediaObj );
//if swfobject.getObjectById fails
if(typeof mediaObj == 'undefined' || mediaObj == null){
console.log('secondary fetch required');
var isIE = navigator.userAgent.match(/MSIE/i);
mediaObj = isIE ? window[playerID] : document[playerID];
}
return mediaObj;
};
Here's the output from my console.log statments:
LOG: fetching media object: [object HTMLObjectElement]
LOG: video-obj-1: ready
LOG: stop called
LOG: pause called
LOG: pause finished
LOG: seek called
LOG: player=[object HTMLObjectElement]
SCRIPT16389: Could not complete the operation due to error 8070000c.
jquery.min.js, line 16 character 29366
The interesting thing is that it appears that the first external interface call 'that.player.pauseMedia()' doesn't have any issue, but the subsequent call to 'that.player.scrubMedia(0)' fails. Another odd thing is that it points to jquery as the source of the error, but there's no call to jquery in those functions.
Here's what i know it's not. It is not an issue where my timing is off. The last line of my actionscript sends a message to the javascript when the flash object has completely loaded. Also i set the parameter 'allowScriptAccess' to 'always' so it's not that either. The actionscript file we use has been used in previous projects so i am 90% certain that that is not the issue.
here's my actionscript anyways. I didn't write actionscript and i'm not too familiar with the language but I tried to put in the parts that seemed most pertinent to my application:
flash.system.Security.allowDomain("*.mydomain.com");
import flash.external.ExternalInterface;
// variables to store local information about the current media
var mediaEmbedServer:String = "www";
var mediaPlayerID:String;
var mediaFile:String;
var mediaDuration:Number;
// variables to be watched by actionscript and message javascript on changes
var mediaPositions:String = "0,0"; // buffer position, scrub position
var mediaStatus:String;
var netStreamClient:Object = new Object();
netStreamClient.onMetaData = metaDataHandler;
netStreamClient.onCuePoint = cuePointHandler;
var connection:NetConnection;
var stream:NetStream;
var media:Video = new Video();
// grab the media's duration when it becomes available
function metaDataHandler(info:Object):void {
mediaDuration = info.duration;
}
function cuePointHandler(info:Object):void {
}
connection = new NetConnection();
connection.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
connection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
try {
var paramName:String;
var paramValue:String;
var paramObject:Object = LoaderInfo(this.root.loaderInfo).parameters;
for (paramName in paramObject) {
paramValue = String(paramObject[paramName]);
switch (paramName){
case "server":
mediaEmbedServer = paramValue;
break
case "playerID":
mediaPlayerID = paramValue;
break
}
}
} catch (error:Error) {
}
if (mediaEmbedServer == "dev" || mediaEmbedServer == "dev2"){
connection.connect("rtmp://media.developmentMediaServer.com/myApp");
} else {
connection.connect("rtmp://media.myMediaServer.com/myApp");
}
function securityErrorHandler(event:SecurityErrorEvent):void {
trace("securityErrorHandler: " + event);
}
function connectStream():void {
stream = new NetStream(connection);
stream.soundTransform = new SoundTransform(1);
stream.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
stream.client = netStreamClient;
media.attachNetStream(stream);
media.width = 720;
media.height = 405;
addChild(media);
}
function netStatusHandler(stats:NetStatusEvent){
switch (stats.info.code){
case "NetConnection.Connect.Success":
connectStream();
break;
case "NetConnection.Call.BadVersion":
case "NetConnection.Call.Failed":
case "NetConnection.Call.Prohibited":
case "NetConnection.Connect.AppShutdown":
case "NetConnection.Connect.Failed":
case "NetConnection.Connect.InvalidApp":
case "NetConnection.Connect.Rejected":
case "NetGroup.Connect.Failed":
case "NetGroup.Connect.Rejected":
case "NetStream.Connect.Failed":
case "NetStream.Connect.Rejected":
case "NetStream.Failed":
case "NetStream.Play.Failed":
case "NetStream.Play.FileStructureInvalid":
case "NetStream.Play.NoSupportedTrackFound":
case "NetStream.Play.StreamNotFound":
case "NetStream.Seek.Failed":
case "NetStream.Seek.InvalidTime":
// report error status and reset javascriptPlay
clearInterval(progressInterval);
messageStatus("error");
break;
default:
// check time through file to determine if media is over
if (stream.time > 0 && stream.time >= (mediaDuration - .25)){
// reset media if it has ended
clearInterval(progressInterval);
stream.play(mediaFile, 0, 0);
messageStatus("finished");
}
}
};
var progressInterval:Number;
// respond to a play/pause request by playing/pausing the current stream
function pauseMedia(){
clearInterval(progressInterval);
if (mediaStatus == 'playing'){
stream.pause();
messageStatus("paused");
}
};
ExternalInterface.addCallback( "pauseMedia", pauseMedia );
// respond to a scrub request by seeking to a position in the media
function scrubMedia(newPosition){
clearInterval(progressInterval);
if (mediaStatus == "playing"){
stream.pause();
messageStatus("paused");
}
stream.seek(newPosition * mediaDuration);
var positionSeconds = newPosition * mediaDuration;
messagePositions(positionSeconds+","+positionSeconds);
};
ExternalInterface.addCallback( "scrubMedia", scrubMedia );
ExternalInterface.call("MediaPlayerReady", mediaPlayerID);
Sounds like an undefined expando property which may be caused by a jQuery IE9 bug. The best way to debug it is to remove the userAgent test and replace it with a check for the object element, such as:
document.getElementsByTagName("object")[0].outerHTML
to see whether the ID attribute is being changed after the first click by jQuery.
I had this problem using JPEGCam, which also uses flash's external interface. My webcam control was being loaded dynamically within a div, and would then throw this error in IE (not firefox or chrome). After moving the initialization of my flash control to document.ready in the parent page, then hiding/showing/moving the control as needed, i was able to work around this exception.
Hope that helps.