boygiandi
boygiandi

Reputation: 680

Can't seek video when playing from MediaSource

I can play a mp4 video by requesting chunk of data using GET request and Range header.

var FILE = 'Momokuri_Ep_09-10_SUB_ITA_dashinit.mp4';
var NUM_CHUNKS = 10;
var chunk_size = 256 * 1024; // 2Kb
var current_chunk = 0;
var file_size = 1;

window.MediaSource = window.MediaSource || window.WebKitMediaSource;
if (!!!window.MediaSource) {
  alert('MediaSource API is not available');
}

var mediaSource = new MediaSource();
var sourceBuffer;

video.src = window.URL.createObjectURL(mediaSource);

function callback(e) {
    sourceBuffer = mediaSource.addSourceBuffer('video/mp4; codecs="avc1.640029, mp4a.40.5"');

    console.log('mediaSource readyState: ' + this.readyState);

    var readChunk = function() {
        GET(FILE, current_chunk, function(uInt8Array) {
            sourceBuffer.appendBuffer(uInt8Array);
        });
    };

    sourceBuffer.addEventListener('update', function(e) {
        if (!sourceBuffer.updating) {
            if (current_chunk == Math.ceil(file_size/chunk_size)-1) {
                if ( mediaSource.readyState!='ended' )
                    mediaSource.endOfStream();
            } else {
                current_chunk++;
                readChunk();
                if (video.paused) {
                    video.play();
                }
            }
        }
    });
    readChunk();
}

mediaSource.addEventListener('sourceopen', callback, false);
mediaSource.addEventListener('webkitsourceopen', callback, false);

mediaSource.addEventListener('webkitsourceended', function(e) {
  console.log('mediaSource readyState: ' + this.readyState);
}, false);

function GET(url, chunk_index, callback) {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    xhr.setRequestHeader('Range', 'bytes='+(chunk_index*chunk_size)+'-'+(++chunk_index*chunk_size-1));
    xhr.responseType = 'arraybuffer';
    xhr.send();

    xhr.onload = function(e) {
        if (xhr.status != 200 && xhr.status != 206) {
            alert("Unexpected status code " + xhr.status + " for " + url);
            return false;
        }

        file_size = parseInt(this.getResponseHeader('content-range').split("/").pop());
        callback(new Uint8Array(xhr.response));
    };
}

But I can't seek the video. So anyone can tell me how to solve these problems :

  1. When I seek video, I can get video.currentTime ( let say 2.5 ) , how to convert it to byte-range request ( how to get the byte offset )
  2. When I got the correct offset and load the correct data from Range GET request, how can I append to sourceBuffer at the right offset

Thanks

Upvotes: 3

Views: 3789

Answers (1)

Erin
Erin

Reputation: 2048

I've been looking for the solution to this myself, and I think I found it.

Have a look at this example.

Whenever a seeking event is emitted from the video element, indicating the user has requested a seek, the old sourcebuffer is closed using sourceBuffer.abort();.

The mediasource then emits a new sourceopen event which allows you to create a new sourcebuffer the same way you did the first time, but this time instead of appending data from the start of the file, you append data from an offset corresponding to the videoElem.currentTime.

How you turn a time offset into a byte offset seems to be left up to you, as it depends on the format of the media you're playing.

In a constant bitrate file, you might be able to get away with basically dividing the file length in bytes by the video length in seconds (and adding a little safety margin). For anything else, you will probably need to parse the file and get timestamps and byte offsets of keyframes.

Upvotes: 3

Related Questions