-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Managed Media Source playback sample adopted from WWDC23 demo (tests/…
…ManagedMediaSource/bipbop.html in github.com/jyavenard/htmltests) with AirPlay blocked
- Loading branch information
1 parent
e39991f
commit fd64215
Showing
18 changed files
with
238 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
*.DS_Store | ||
*.Makefile | ||
*.host.mk | ||
*.ncb | ||
*.ninja | ||
*.props | ||
*.pyc | ||
*.rules | ||
*.scons | ||
*.sdf | ||
*.sln | ||
*.suo | ||
*.target.mk | ||
*.targets | ||
*.user | ||
*.vcproj | ||
*.vcxproj | ||
*.vcxproj.filters | ||
*.vpj | ||
*.vpw | ||
*.vpwhistu | ||
*.vs | ||
*.vtg | ||
*.xcodeproj | ||
*.xcworkspace | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<meta charset="utf-8" /> | ||
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1" /> | ||
<title>Managed MediaSource API Demo</title> | ||
</head> | ||
|
||
<body onload="startUp()"> | ||
|
||
<h3>Appending .mp4 video chunks using the Managed Media Source API</h3> | ||
|
||
<section> | ||
</section> | ||
|
||
<script src="./mediasource.js"></script> | ||
<script src="./main.js"></script> | ||
|
||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
'use strict' | ||
|
||
function runWithManagedMSE(testFunction, id = 'log') { | ||
var el = document.createElement('video'); | ||
el.disableRemotePlayback = true; | ||
el.controls = true; | ||
document.body.appendChild(el); | ||
|
||
var log = document.createElement('pre'); | ||
log.setAttribute('id', id); | ||
document.body.appendChild(log); | ||
|
||
var logger = new Logger(id); | ||
|
||
if (!!!window.ManagedMediaSource) { | ||
info('Managed MediaSource API is not available'); | ||
return; | ||
} | ||
var ms = new ManagedMediaSource(); | ||
|
||
el.src = URL.createObjectURL(ms); | ||
el.preload = 'auto'; | ||
|
||
testFunction(ms, el); | ||
} | ||
|
||
function logEvents(events, target) { | ||
// Log events for debugging. | ||
function logEvent(e) { | ||
var v = e.target; | ||
info('got ' + e.type + ' event'); | ||
} | ||
events.forEach(function(e) { | ||
target.addEventListener(e, logEvent); | ||
}); | ||
} | ||
|
||
function startUp() { | ||
runWithManagedMSE(async (ms, el) => { | ||
// Log events for debugging. | ||
logEvents(['suspend', 'play', 'canplay', 'canplaythrough', 'loadstart', 'loadedmetadata', | ||
'loadeddata', 'playing', 'ended', 'error', 'stalled', 'emptied', 'abort', | ||
'waiting', 'pause', 'durationchange', 'seeking', 'seeked'], el); | ||
logEvents(['sourceopen', 'ended', 'startstreaming', 'endstreaming'], ms); | ||
|
||
await once(ms, 'sourceopen'); | ||
ok(true, 'Receive a sourceopen event'); | ||
var sb = ms.addSourceBuffer('video/mp4; codecs="mp4a.40.2,avc1.4d4015"'); | ||
await fetchAndLoad(sb, './media/bipbopinit', [''], '.mp4'); | ||
await fetchAndLoad(sb, './media/bipbop', range(1, 13), '.m4s'); | ||
ms.endOfStream(); | ||
}); | ||
} | ||
window.startUp = startUp |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
// Helpers for Media Source Extensions tests | ||
var _logger; | ||
|
||
function Logger(id) { | ||
this.el = document.getElementById(id); | ||
_logger = this; | ||
} | ||
var log_start = Date.now(); | ||
Logger.prototype.log = function(msg) { | ||
var fragment = document.createDocumentFragment(); | ||
fragment.appendChild(document.createTextNode((Date.now() - log_start) + ': ' + msg)); | ||
fragment.appendChild(document.createElement('br')); | ||
this.el.appendChild(fragment); | ||
}; | ||
|
||
Logger.prototype.clear = function() { | ||
this.el.textContent = ''; | ||
}; | ||
|
||
function info(message) { | ||
_logger.log(message); | ||
} | ||
|
||
function ok(value, message) { | ||
_logger.log("ok" + (value ? "(true) " : "(false) ") + message); | ||
} | ||
|
||
function is(value1, value2, message) { | ||
var result = value1 == value2; | ||
_logger.log("is(" + value1 + " == " + value2 + "): " + result + " (" + message + ")"); | ||
} | ||
|
||
function isfuzzy(value1, value2, fuzz, message) { | ||
var result = (value1 >= value2 - fuzz && value1 <= value2 + fuzz); | ||
_logger.log("" + value1 + " == " + value2 + " ~" + fuzz + ": " + result + " (" + message + ")"); | ||
} | ||
|
||
function runWithMSE(testFunction, id = 'log') { | ||
window.onload = function() { | ||
var ms = new MediaSource(); | ||
|
||
var el = document.createElement("video"); | ||
el.src = URL.createObjectURL(ms); | ||
el.preload = "auto"; | ||
|
||
document.body.appendChild(el); | ||
|
||
var log = document.createElement("pre"); | ||
log.setAttribute("id", id); | ||
document.body.appendChild(log); | ||
|
||
var logger = new Logger(id); | ||
|
||
testFunction(ms, el); | ||
}; | ||
} | ||
|
||
function fetchWithXHR(uri, onLoadFunction) { | ||
var p = new Promise(function(resolve, reject) { | ||
var xhr = new XMLHttpRequest(); | ||
xhr.open("GET", uri, true); | ||
xhr.responseType = "arraybuffer"; | ||
xhr.addEventListener("load", function () { | ||
is(xhr.status, 200, "fetchWithXHR load uri='" + uri + "' status=" + xhr.status); | ||
resolve(xhr.response); | ||
}); | ||
xhr.send(); | ||
}); | ||
|
||
if (onLoadFunction) { | ||
p.then(onLoadFunction); | ||
} | ||
|
||
return p; | ||
}; | ||
|
||
function range(start, end) { | ||
var rv = []; | ||
for (var i = start; i < end; ++i) { | ||
rv.push(i); | ||
} | ||
return rv; | ||
} | ||
|
||
function once(target, name, cb) { | ||
var p = new Promise(function(resolve, reject) { | ||
target.addEventListener(name, function() { | ||
target.removeEventListener(name, arguments.callee); | ||
resolve(); | ||
}); | ||
}); | ||
if (cb) { | ||
p.then(cb); | ||
} | ||
return p; | ||
} | ||
|
||
function timeRangeToString(r) { | ||
var str = "TimeRanges: "; | ||
for (var i = 0; i < r.length; i++) { | ||
str += "[" + r.start(i) + ", " + r.end(i) + ")"; | ||
} | ||
return str; | ||
} | ||
|
||
function loadSegment(sb, typedArrayOrArrayBuffer) { | ||
var typedArray = (typedArrayOrArrayBuffer instanceof ArrayBuffer) ? new Uint8Array(typedArrayOrArrayBuffer) | ||
: typedArrayOrArrayBuffer; | ||
info(`Loading buffer: [${typedArray.byteOffset}, ${typedArray.byteOffset + typedArray.byteLength})`); | ||
var beforeBuffered = timeRangeToString(sb.buffered); | ||
return new Promise(function(resolve, reject) { | ||
once(sb, 'update').then(function() { | ||
var afterBuffered = timeRangeToString(sb.buffered); | ||
info(`SourceBuffer buffered ranges grew from ${beforeBuffered} to ${afterBuffered}`); | ||
resolve(); | ||
}); | ||
sb.appendBuffer(typedArray); | ||
}); | ||
} | ||
|
||
function fetchAndLoad(sb, prefix, chunks, suffix) { | ||
|
||
// Fetch the buffers in parallel. | ||
var buffers = {}; | ||
var fetches = []; | ||
for (var chunk of chunks) { | ||
fetches.push(fetchWithXHR(prefix + chunk + suffix).then(((c, x) => buffers[c] = x).bind(null, chunk))); | ||
} | ||
|
||
// Load them in series, as required per spec. | ||
return Promise.all(fetches).then(function() { | ||
var rv = Promise.resolve(); | ||
for (var chunk of chunks) { | ||
rv = rv.then(loadSegment.bind(null, sb, buffers[chunk])); | ||
} | ||
return rv; | ||
}); | ||
} |