blob: cab4d58ffbcf48c91d56b26d07de443989884dfc [file] [log] [blame]
// Copyright 2018 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The demo simply plays a video using get parameters in the form of:
// .../multi-video-demo.html?audio=filename_in_same_folder&video=filename_in_same_folder&instances=2
var kAudioChunkSize = 512 * 1024;
var kVideoChunkSize = 2 * 1024 * 1024;
var kEndOfStreamOffset = -1;
class Player {
constructor(audio_url, video_url) {
this.audio_offset = 0;
this.audio_url = audio_url;
this.video_offset = 0;
this.video_url = video_url;
this.video_tag = this.createVideoElement();
this.status = this.createStatusElement(this.video_tag);
this.video_tag.src = '';
this.video_tag.load();
this.mediasource = new MediaSource;
this.mediasource.addEventListener('sourceopen', this.onsourceopen.bind(this));
this.video_tag.src = window.URL.createObjectURL(this.mediasource);
}
onsourceopen() {
if (this.video_url.endsWith('.mp4')) {
this.video_source_buffer = this.mediasource.addSourceBuffer(
'video/mp4; codecs="avc1.640028"');
} else {
this.video_source_buffer = this.mediasource.addSourceBuffer(
'video/webm; codecs="vp9"');
}
this.audio_source_buffer = this.mediasource.addSourceBuffer(
'audio/mp4; codecs="mp4a.40.2"');
this.tryToDownloadAudioData();
}
downloadAndAppend(url, begin, end, source_buffer, callback) {
var xhr = new XMLHttpRequest;
xhr.open('GET', url, true);
xhr.responseType = 'arraybuffer';
xhr.addEventListener('load', function(e) {
var data = new Uint8Array(e.target.response);
var onupdateend = function() {
source_buffer.removeEventListener('updateend', onupdateend);
callback(data.length);
};
source_buffer.addEventListener('updateend', onupdateend);
source_buffer.appendBuffer(data);
console.log('append ' + data.length + ' bytes from '
+ url);
});
xhr.setRequestHeader('Range', ('bytes=' + begin +'-' + end));
xhr.send();
}
isTrackNeedAudioData() {
if (this.audio_offset == kEndOfStreamOffset) {
return false;
}
var buffer_range_size = this.audio_source_buffer.buffered.length;
if (buffer_range_size == 0) {
return true;
}
var start = this.audio_source_buffer.buffered.start(buffer_range_size - 1);
var end = this.audio_source_buffer.buffered.end(buffer_range_size - 1);
console.log('audio ' + start + '/' + end + ' ' + this.video_tag.currentTime)
return end - this.video_tag.currentTime <= 20;
}
isTrackNeedVideoData() {
if (this.video_offset == kEndOfStreamOffset) {
return false;
}
var buffer_range_size = this.video_source_buffer.buffered.length;
if (buffer_range_size == 0) {
return true;
}
var start = this.video_source_buffer.buffered.start(buffer_range_size - 1);
var end = this.video_source_buffer.buffered.end(buffer_range_size - 1);
console.log('video ' + start + '/' + end + ' ' + this.video_tag.currentTime)
return end - this.video_tag.currentTime <= 20;
}
tryToDownloadAudioData() {
if (!this.isTrackNeedAudioData()) {
if (this.isTrackNeedVideoData()) {
this.tryToDownloadVideoData();
} else {
window.setTimeout(this.tryToDownloadAudioData.bind(this), 1000);
}
return;
}
this.downloadAndAppend(
this.audio_url, this.audio_offset,
this.audio_offset + kAudioChunkSize - 1, this.audio_source_buffer,
this.onAudioDataDownloaded.bind(this));
}
onAudioDataDownloaded(length) {
if (length != kAudioChunkSize) {
this.audio_offset = kEndOfStreamOffset;
}
if (this.audio_offset != kEndOfStreamOffset) {
this.audio_offset += kAudioChunkSize;
}
this.tryToDownloadVideoData();
}
tryToDownloadVideoData() {
if (!this.isTrackNeedVideoData()) {
if (this.isTrackNeedAudioData()) {
this.tryToDownloadAudioData();
} else {
window.setTimeout(this.tryToDownloadVideoData.bind(this), 1000);
}
return;
}
this.downloadAndAppend(
this.video_url, this.video_offset,
this.video_offset + kVideoChunkSize - 1, this.video_source_buffer,
this.onVideoDataDownloaded.bind(this));
}
onVideoDataDownloaded(length) {
if (length != kVideoChunkSize) {
this.video_offset = kEndOfStreamOffset;
}
if (this.video_offset != kEndOfStreamOffset) {
this.video_offset += kVideoChunkSize;
}
this.tryToDownloadAudioData();
}
createVideoElement() {
var video = document.createElement('video');
video.autoplay = true;
video.style.width = '320px';
video.style.height = '240px';
document.body.appendChild(video);
return video;
}
createStatusElement(video) {
var status = document.createElement('div');
document.body.appendChild(status);
video.addEventListener('timeupdate', function () {
status.textContent = 'time: ' + video.currentTime.toFixed(2);
});
return status;
}
}
function main() {
var get_parameters = window.location.search.substr(1).split('&');
var audio_url, video_url, instances = 2;
for (var param of get_parameters) {
splitted = param.split('=');
if (splitted[0] == 'audio') {
audio_url = splitted[1];
} else if (splitted[0] == 'video') {
video_url = splitted[1];
} else if (splitted[0] == 'instances') {
instances = splitted[1];
}
}
if (audio_url && video_url) {
for (var i = 0; i < instances; ++i) {
new Player(audio_url, video_url);
}
} else {
status.textContent = "invalid get parameters " +
window.location.search.substr(1);
}
}
main();