Reputation: 11
streaming is working fine when range is sent in sequential order but when user seeks to some position where data has not streamed yet, playback stops and a loader keeps loading in Firefox browser and in other browsers too. I am using source buffer in segment mode for now. I have also tried changing timestampOffset but not able to calculate time from blob.
Also I have not divided file into sub files. I am using a single file and sending http requests with range header and in backend it reads the data from file according to range given and returns data with 206 partial content header.
Here is the code I have written.
var buffer_status = false;
var buffered_bytes = 0;
var load_bytes = 524288;
var next_bytes = 1048576+1048576;
var total_content_length = 0;
var media_source = null;
var source_buffer = null;
var name = "4524e802cf.webm";//"a03633a097.webm";
var url = "/Requester.php?call_function=run";
var control = null;
var blob = null;
var progressive_array = [];
var percentBuffered = 0;
var trig = 0;
control = document.getElementById('control');
media_source = new MediaSource();
control.src = URL.createObjectURL(media_source);
var call_source_buffer = async () => {
source_buffer = await new Promise((resolve, reject) => {
const get_source_buffer = () => {
try{
var mime = 'video/webm; codecs="vp9,opus"';
const source_buffer = media_source.addSourceBuffer(mime);
// source1_buffer.mode = "sequence";
// console.log('buffer added');
resolve(source_buffer);
}
catch(error){
console.error(error);
}
};
if(media_source.readyState == "open"){
get_source_buffer();
}
else{
media_source.addEventListener('sourceopen',get_source_buffer);
}
});
};
async function load_data(){
if(!buffer_status){
try{
var response = await fetch(url,{
headers: {
'Range': "bytes="+buffered_bytes+"-"+load_bytes,
'name': name
}
});
blob = await response.blob();
var array_data = await blob.arrayBuffer();
total_content_length = response.headers.get('Content-range');
total_content_length = total_content_length.substr(total_content_length.indexOf("/")+1,total_content_length.length);
source_buffer.appendBuffer(array_data);
buffered_bytes = buffered_bytes + load_bytes;
}
catch(error){
console.error(error);
}
}
else{
console.log("already buffering data");
}
source_buffer.addEventListener('updateend',function(){
if(control.paused){
control.play();
}
});
}
call_source_buffer();
load_data();
control.addEventListener('waiting', async () => {
if(progressive_array.length == 0){
if(buffered_bytes == (total_content_length-1)){
console.log('data completed');
return false;
}
if((buffered_bytes+next_bytes) > total_content_length){
if(!buffer_status){
buffer_status = true;
try{
var response = await fetch(url,{
headers: {
'Range': "bytes="+(buffered_bytes+1)+"-"+total_content_length,
'name': name
}
});
blob = await response.blob();
var array_data = await blob.arrayBuffer();
progressive_array.push(array_data);
buffer_status = false;
var remaining_bytes = total_content_length - (buffered_bytes+1);
buffered_bytes = buffered_bytes + remaining_bytes;
}
catch(error){
console.error(error);
}
}
else{
console.log("already buffering data");
}
}
else{
if(!buffer_status){
buffer_status = true;
try{
var response = await fetch(url,{
headers: {
'Range': "bytes="+(buffered_bytes+1)+"-"+(buffered_bytes+next_bytes),
// 'Range': `bytes=${buffered_bytes+1}-${currentBytes}`,
'name': name
}
});
blob = await response.blob();
var array_data = await blob.arrayBuffer();
progressive_array.push(array_data);
buffer_status = false;
buffered_bytes = buffered_bytes + next_bytes;
}
catch(error){
console.error(error);
}
}
else{
console.log("already buffering data");
}
}
var data_percent = parseFloat((buffered_bytes / total_content_length) * 100);
if(data_percent > 100){
data_percent = 100;
}
document.getElementById('stream_slider').style.width = `${data_percent}%`;
}
else{
try{
if(!source_buffer.updating){
// console.log('appended');
source_buffer.appendBuffer(progressive_array[0]);
progressive_array = [];
}
}
catch(error){
console.error(error);
}
if(buffered_bytes == (total_content_length-1)){
console.log('data completed');
return false;
}
if((buffered_bytes+next_bytes) > total_content_length){
if(!buffer_status){
buffer_status = true;
try{
var response = await fetch(url,{
headers: {
'Range': `bytes=${buffered_bytes+1}-${total_content_length-1}`,
'name': name
}
});
blob = await response.blob();
var array_data = await blob.arrayBuffer();
progressive_array.push(array_data);
buffer_status = false;
var remaining_bytes = total_content_length - (buffered_bytes+1);
buffered_bytes = buffered_bytes + remaining_bytes;
}
catch(error){
console.error(error);
}
}
else{
console.log("already buffering data");
}
}
else{
// console.log('ran');
if(!buffer_status){
buffer_status = true;
try{
var response = await fetch(url,{
headers: {
'Range': "bytes="+(buffered_bytes+1)+"-"+(buffered_bytes+next_bytes),
// 'Range': `bytes=${buffered_bytes+1}-${currentBytes}`,
'name': name
}
});
blob = await response.blob();
var array_data = await blob.arrayBuffer();
progressive_array.push(array_data);
buffer_status = false;
console.log(array_data);
buffered_bytes = buffered_bytes + next_bytes;
// buffered_bytes = currentBytes;
}
catch(error){
console.error(`err : ${error}`);
}
}
else{
console.log("already buffering data");
}
}
var data_percent = parseFloat((buffered_bytes / total_content_length) * 100);
if(data_percent > 100){
data_percent = 100;
}
document.getElementById('stream_slider').style.width = `${data_percent}%`;
}
});
document.getElementById('play_btn').addEventListener('click', (evt) => {
control.play();
});
document.getElementById('pause_btn').addEventListener('click', (evt) => {
control.pause();
});
control.addEventListener('play', (evt) => {
document.getElementById('play_btn').style.display = 'none';
document.getElementById('pause_btn').style.display = 'inline';
});
control.addEventListener('pause', (evt) => {
document.getElementById('play_btn').style.display = 'inline';
document.getElementById('pause_btn').style.display = 'none';
});
control.addEventListener('timeupdate', (evt) => {
var current_time = parseFloat(control.currentTime);
var duration = parseFloat(control.duration);
var current_percent = parseFloat((current_time / duration) * 100);
// document.getElementById('current_slider').style.width = `${current_percent}%`;
// document.getElementById('current_slider').value = `${current_percent}`;
var current_time = parseInt(control.currentTime);
var duration = parseInt(control.duration);
var min = parseInt((current_time / 60));
var sec = parseInt((current_time % 60));
if(min.toString().length == 1){
min = `0${min}`;
}
if(sec.toString().length == 1){
sec = `0${sec}`;
}
document.getElementById('current_time_label').innerHTML = `${min}:${sec}`;
var min = parseInt((duration / 60));
var sec = parseInt((duration % 60));
if(min.toString().length == 1){
min = `0${min}`;
}
if(sec.toString().length == 1){
sec = `0${sec}`;
}
document.getElementById('duration_label').innerHTML = `${min}:${sec}`;
});
document.getElementById("current_slider").addEventListener("change",(evt)=>{
var cur_min = control.duration * (evt.currentTarget.value/100);
control.currentTime = parseInt(cur_min);
});
control.addEventListener("seeking", async function(evt){
console.log("seeking");
var from = (((control.currentTime / control.duration) * 100)/100) * total_content_length;
var to = from+next_bytes;
from = parseInt(from);
to = (parseInt(to) > total_content_length)?total_content_length:parseInt(to);
console.log("from: " + from + ", to: " + to);
if(!buffer_status){
buffer_status = true;
try{
var response = await fetch(url,{
headers: {
'Range': `bytes=${from}-${to}`,
'name': name
}
});
blob = await response.blob();
var array_data = await blob.arrayBuffer();
if(!source_buffer.updating){
source_buffer.appendBuffer(array_data);
// source_buffer.timestampOffset = control.currentTime;
}
buffer_status = false;
buffered_bytes = to;
console.log("appended");
}
catch(error){
console.error(error);
}
}
else{
console.log("already buffering data");
}
});
const getDuration = (blob) => {
return new Promise((res) => {
const tempVidElem = document.createElement('video');
tempVidElem.onloadedmetadata = () => {
res(tempVidElem.duration);
URL.revokeObjectURL(tempVidElem.src);
};
tempVidElem.src = URL.createObjectURL(blob);
console.log(tempVidElem.duration);
});
};
Upvotes: 1
Views: 177