Open dstaubsauger opened 8 years ago
Yeah, I have found the some problem. Can anybody help? I had try to fix it, but it didn't work.
……Author knew the issue. See here, https://hacks.mozilla.org/2015/07/streaming-media-on-demand-with-media-source-extensions/
My demo is a little naive and has a few issues:
It doesn’t show how to properly handle seeking during playback. It assumes bandwidth is constant (always fetching the next segment at 80% playback of the previous segment), which it isn’t. It starts off by loading only one segment (it might be better to fetch the first few, then wait to fetch the rest). It doesn’t switch between segments of varying resolution, instead only fetching segments of one quality. It doesn’t remove segments (part of the MSE API), although this can be helpful on memory constrained devices. Unfortunately, this requires you to re-fetch content when seeking backwards.
@IShinji @dstaubsauger
I try this code
`var video = document.querySelector("video");
var assetURL = "frag_bunny.mp4";
var mimeCodec = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';
var totalSegments;
var segmentLength = 0;
var segmentDuration = 0;
var seeking = false;
var bytesFetched = 0;
var requestedSegments = [];
var mediaSource = null;
if ("MediaSource" in window && MediaSource.isTypeSupported(mimeCodec)) {
mediaSource = new MediaSource();
video.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener("sourceopen", sourceOpen);
} else {
console.error("Unsupported MIME type or codec: ", mimeCodec);
}
var sourceBuffer = null;
function sourceOpen(_) {
sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
sourceBuffer.mode = "sequence";
getFileLength(assetURL, function(fileLength) {
// console.log((fileLength / 1024 / 1024).toFixed(2), "MB");
totalSegments = (fileLength / 1024 / 1024).toFixed(0);
for (var i = 0; i < totalSegments; ++i) requestedSegments[i] = false;
segmentLength = Math.round(fileLength / totalSegments);
fetchRange(assetURL, 0, segmentLength, appendSegment);
requestedSegments[0] = true;
video.addEventListener("timeupdate", checkBuffer);
video.addEventListener("canplay", function() {
segmentDuration = video.duration / totalSegments;
const promise = video.play();
if (promise !== undefined) {
promise
.then(_ => {
console.log(_);
// Autoplay started!
})
.catch(error => {
// Autoplay was prevented.
// Show a "Play" button so that user can start playback.
});
}
});
video.addEventListener("seeking", debounce(handleSeek, 500));
video.addEventListener("seeked", onSeeked);
});
}
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this;
var args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
}
function getFileLength(url, cb) {
var xhr = new XMLHttpRequest();
xhr.open("head", url);
xhr.onload = function() {
cb(xhr.getResponseHeader("content-length"));
};
xhr.send();
}
function onSeeked() {
console.log("seeked");
}
function fetchRange(url, start, end, cb) {
return new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest();
xhr.open("get", url);
xhr.responseType = "arraybuffer";
xhr.setRequestHeader("Range", "bytes=" + start + "-" + end);
xhr.onload = function() {
console.log("fetched bytes: ", start, end);
bytesFetched += end - start + 1;
resolve(cb(xhr.response));
};
xhr.send();
});
}
function appendSegment(chunk) {
return sourceBuffer.appendBuffer(chunk);
}
async function checkBuffer() {
console.log("check Buffer");
if (seeking) {
return true;
}
var nextSegment = getNextSegment();
if (nextSegment === totalSegments && haveAllSegments()) {
mediaSource.endOfStream();
video.removeEventListener("timeupdate", checkBuffer);
} else if (shouldFetchNextSegment(nextSegment)) {
requestedSegments[nextSegment] = true;
await fetchRange(
assetURL,
bytesFetched,
bytesFetched + segmentLength,
appendSegment
);
}
}
async function handleSeek() {
console.log("seeking");
seeking = true;
var nextSegment = getNextSegment();
if (nextSegment === totalSegments && haveAllSegments()) {
mediaSource.endOfStream();
video.removeEventListener("timeupdate", checkBuffer);
} else {
// sourceBuffer.abort();
for (let segment = 1; segment < nextSegment; segment++) {
if (shouldFetchNextSegment(segment)) {
requestedSegments[segment] = true;
// console.log({ bytesFetched, next: bytesFetched + segmentLength });
await fetchRange(
assetURL,
bytesFetched,
bytesFetched + segmentLength,
appendSegment
);
}
}
}
seeking = false;
}
function seek(e) {
if (mediaSource.readyState === "open") {
handleSeek();
} else {
console.log("seek but not open?");
console.log(mediaSource.readyState);
}
}
function getNextSegment() {
return ((video.currentTime / segmentDuration) | 0) + 1;
}
function haveAllSegments() {
return requestedSegments.every(function(val) {
return !!val;
});
}
function shouldFetchNextSegment(nextSegment) {
// console.log(nextSegment);
// console.log({
// nextSegment,
// currentTime: video.currentTime,
// val: segmentDuration * nextSegment * 0.1,
// bool: requestedSegments[nextSegment]
// });
return (
video.currentTime > segmentDuration * nextSegment * 0.1 &&
requestedSegments[nextSegment] == false
);
}`
I saw there was some code listening to the seek event, so i tried it, but:
Expected behaviour: Jumping to a certain point loads the video at that point (if not already loaded) and continues playing/loading from there
Actual behaviour: Video only plays if that point is already loaded. No subsequent parts are loaded. This also applies when seeking backwards.