### Media Source Extensions JavaScript Example Source: https://github.com/w3c/html-media/blob/main/media-source/media-source-lc.html Provides an example of using the Media Source Extensions API in JavaScript to manage media playback. It demonstrates setting up a MediaSource, adding a SourceBuffer, appending initialization and media segments, and handling playback events like seeking and progress. ```javascript function onSourceOpen(videoTag, e) { var mediaSource = e.target; var sourceBuffer = mediaSource.addSourceBuffer('video/webm; codecs="vorbis,vp8"'); videoTag.addEventListener('seeking', onSeeking.bind(videoTag, mediaSource)); videoTag.addEventListener('progress', onProgress.bind(videoTag, mediaSource)); var initSegment = GetInitializationSegment(); if (initSegment == null) { // Error fetching the initialization segment. Signal end of stream with an error. mediaSource.endOfStream("network"); return; } // Append the initialization segment. var firstAppendHandler = function(e) { var sourceBuffer = e.target; sourceBuffer.removeEventListener('updateend', firstAppendHandler); // Append some initial media data. appendNextMediaSegment(mediaSource); }; sourceBuffer.addEventListener('updateend', firstAppendHandler); sourceBuffer.appendBuffer(initSegment); } function appendNextMediaSegment(mediaSource) { if (mediaSource.readyState == "ended") return; // If we have run out of stream data, then signal end of stream. if (!HaveMoreMediaSegments()) { mediaSource.endOfStream(); return; } // Make sure the previous append is not still pending. if (mediaSource.sourceBuffers[0].updating) return; var mediaSegment = GetNextMediaSegment(); if (!mediaSegment) { // Error fetching the next media segment. mediaSource.endOfStream("network"); return; } mediaSource.sourceBuffers[0].appendBuffer(mediaSegment); } function onSeeking(mediaSource, e) { var video = e.target; // Abort current segment append. mediaSource.sourceBuffers[0].abort(); // Notify the media segment loading code to start fetching data at the // new playback position. SeekToMediaSegmentAt(video.currentTime); // Append a media segment from the new playback position. appendNextMediaSegment(mediaSource); } function onProgress(mediaSource, e) { appendNextMediaSegment(mediaSource); } ``` ```html ``` -------------------------------- ### Seeking Behavior and Example Fixes Source: https://github.com/w3c/html-media/blob/main/media-source/media-source.html Addresses clarifications and fixes related to seeking behavior and example code within the specification. ```APIDOC Seeking and Example Updates: - Clarified seeking behavior in Section 2.4.3. - Fixed example to work when seeking in the 'ended' state. ``` -------------------------------- ### Complete EME Event Handling Example Source: https://github.com/w3c/html-media/blob/main/encrypted-media/encrypted-media.html A full JavaScript example demonstrating the use of various EME events and methods for handling encrypted media. It includes session creation, message handling, key updates, and error reporting. ```javascript var licenseUrl; var serverCertificate; var mediaKeys; // See previous examples for implementations of these functions. // createSupportedKeySystem() additionally sets renewalUrl. function createSupportedKeySystem() { /* ... */ } function handleInitData(event) { /* ... */ } // This replaces the implementation in the previous example. function makeNewRequest(mediaKeys, initDataType, initData) { var keySession = mediaKeys.createSession(); keySession.addEventListener("message", handleMessage, false); keySession.addEventListener("keyschange", handleKeysChange, false); keySession.closed.then( console.log.bind(console, "Session closed") ); keySession.generateRequest(initDataType, initData).catch( console.error.bind(console, "Unable to create or initialize key session") ); } function handleMessageResponse(keySession, response) { var license = new Uint8Array(response); keySession.update(license).catch( function(err) { console.error("update() failed: " + err); } ); } function sendMessage(type, message, keySession) { var url = licenseUrl; if (type == "licenserenewal") url = renewalUrl; xmlhttp = new XMLHttpRequest(); xmlhttp.keySession = keySession; xmlhttp.open("POST", url); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4) handleMessageResponse(xmlhttp.keySession, xmlhttp.response); } xmlhttp.send(message); } function handleMessage(event) { sendMessage(event.messageType, event.message, event.target); } function handleKeysChange(event) { event.target.getUsableKeyIds().then( function(keyIdSequence) { // Process keyIdSequence and respond appropriately. } ).catch( console.error.bind(console, "Failed handling usable keys change") ); } function handleError(event) { // Report and do some bookkeeping with event.target.sessionId if necessary. } createSupportedKeySystem().then( function(createdMediaKeys) { mediaKeys = createdMediaKeys; var video = document.getElementById("v"); video.src = "foo.webm"; if (serverCertificate) mediaKeys.setServerCertificate(serverCertificate); return video.setMediaKeys(mediaKeys); } ).catch( console.error.bind(console, "Failed to create and initialize a MediaKeys object") ); ``` -------------------------------- ### Media Source Extensions Example Source: https://github.com/w3c/html-media/blob/main/media-source/media-source-fpwd.html Demonstrates the usage of Media Source Extensions API in JavaScript to play media. It includes setting up a MediaSource, adding a SourceBuffer, appending initialization and media segments, and handling playback events like seeking and progress. ```javascript var video = document.getElementById('v'); var mediaSource = new MediaSource(); mediaSource.addEventListener('sourceopen', onSourceOpen.bind(this, video)); video.src = window.URL.createObjectURL(mediaSource); function onSourceOpen(videoTag, e) { var mediaSource = e.target; var sourceBuffer = mediaSource.addSourceBuffer('video/webm; codecs="vorbis,vp8"'); videoTag.addEventListener('seeking', onSeeking.bind(videoTag, mediaSource)); videoTag.addEventListener('progress', onProgress.bind(videoTag, mediaSource)); var initSegment = GetInitializationSegment(); if (initSegment == null) { // Error fetching the initialization segment. Signal end of stream with an error. mediaSource.endOfStream("network"); return; } // Append the initialization segment. var firstAppendHandler = function(e) { var sourceBuffer = e.target; sourceBuffer.removeEventListener('appendend', firstAppendHandler); // Append some initial media data. appendNextMediaSegment(mediaSource); }; sourceBuffer.addEventListener('appendend', firstAppendHandler); sourceBuffer.appendArrayBuffer(initSegment); } function appendNextMediaSegment(mediaSource) { if (mediaSource.readyState == "ended") return; // If we have run out of stream data, then signal end of stream. if (!HaveMoreMediaSegments()) { mediaSource.endOfStream(); return; } // Make sure the previous append is not still pending. if (mediaSource.sourceBuffers[0].appending) return; var mediaSegment = GetNextMediaSegment(); if (!mediaSegment) { // Error fetching the next media segment. mediaSource.endOfStream("network"); return; } mediaSource.sourceBuffers[0].appendArrayBuffer(mediaSegment); } function onSeeking(mediaSource, e) { var video = e.target; // Abort current segment append. mediaSource.sourceBuffers[0].abort(); // Notify the media segment loading code to start fetching data at the // new playback position. SeekToMediaSegmentAt(video.currentTime); // Append a media segment from the new playback position. appendNextMediaSegment(mediaSource); } function onProgress(mediaSource, e) { appendNextMediaSegment(mediaSource); } // Placeholder functions for demonstration purposes function GetInitializationSegment() { // In a real scenario, this would fetch the initialization segment. console.log('Fetching initialization segment...'); return new Uint8Array([0x01, 0x02, 0x03]); // Dummy data } function HaveMoreMediaSegments() { // In a real scenario, this would check if there's more media data. console.log('Checking for more media segments...'); return false; // For this example, we stop after the init segment. } function GetNextMediaSegment() { // In a real scenario, this would fetch the next media segment. console.log('Fetching next media segment...'); return null; // No more segments in this example. } function SeekToMediaSegmentAt(currentTime) { // In a real scenario, this would adjust segment fetching based on currentTime. console.log('Seeking to time:', currentTime); } ``` -------------------------------- ### Update Example Code with 'updating' Attribute Source: https://github.com/w3c/html-media/blob/main/media-source/media-source-wd.html Updates example code snippets to reflect the use of the 'updating' attribute instead of the deprecated 'appending' attribute. This ensures examples are current with the specification. ```APIDOC Example Usage: - Instead of: `if (sourceBuffer.appending) { ... }` - Use: `if (sourceBuffer.updating) { ... }` ``` -------------------------------- ### Initialize MediaKeys and Set Media Source (JavaScript) Source: https://github.com/w3c/html-media/blob/main/encrypted-media/encrypted-media-wd.html This example demonstrates initializing a MediaKeys object and associating it with a video element. It shows how to create MediaKeys, set a server certificate, and then apply it to the video source, handling potential errors during the process. It relies on helper functions like selectKeySystem and createSession. ```javascript var keySystem; var licenseUrl; var serverCertificate; var mediaKeys; // See the previous example for implementations of these functions. function selectKeySystem() { /* ... */ } function createSession(mediaKeys, initDataType, initData) { /* ... */ } function licenseRequestReady(event) { /* ... */ } function handleInitData(event) { createSession(mediaKeys, event.initDataType, event.initData); } selectKeySystem(); MediaKeys.create(keySystem).then( function(createdMediaKeys) { mediaKeys = createdMediaKeys; var video = document.getElementById("v"); video.src = "foo.webm"; if (serverCertificate) mediaKeys.setServerCertificate(serverCertificate); return video.setMediaKeys(mediaKeys); } ).catch( console.error.bind(console, "Unable to create or initialize key session") ); ``` -------------------------------- ### Key IDs Initialization Data JSON Example Source: https://github.com/w3c/html-media/blob/main/encrypted-media/keyids-format-respec.html Example of the JSON format for initialization data, containing an array of base64url encoded key IDs ('kids') used for license requests in Encrypted Media Extensions. ```json { "kids": [ "67ef0gd8pvfd0", "77ef0gd8pvfd0" ] } ``` -------------------------------- ### Media Source Extensions Video Streaming Example Source: https://github.com/w3c/html-media/blob/main/media-source/media-source.html Demonstrates how to use the Media Source Extensions API to stream video content. It includes functions for handling source opening, appending media segments, and responding to seeking and progress events. ```javascript function onSourceOpen(videoTag, e) { var mediaSource = e.target; if (mediaSource.sourceBuffers.length > 0) return; var sourceBuffer = mediaSource.addSourceBuffer('video/webm; codecs="vorbis,vp8"'); videoTag.addEventListener('seeking', onSeeking.bind(videoTag, mediaSource)); videoTag.addEventListener('progress', onProgress.bind(videoTag, mediaSource)); var initSegment = GetInitializationSegment(); if (initSegment == null) { // Error fetching the initialization segment. Signal end of stream with an error. mediaSource.endOfStream("network"); return; } // Append the initialization segment. var firstAppendHandler = function(e) { var sourceBuffer = e.target; sourceBuffer.removeEventListener('updateend', firstAppendHandler); // Append some initial media data. appendNextMediaSegment(mediaSource); }; sourceBuffer.addEventListener('updateend', firstAppendHandler); sourceBuffer.appendBuffer(initSegment); } function appendNextMediaSegment(mediaSource) { if (mediaSource.readyState == "closed") return; // If we have run out of stream data, then signal end of stream. if (!HaveMoreMediaSegments()) { mediaSource.endOfStream(); return; } // Make sure the previous append is not still pending. if (mediaSource.sourceBuffers[0].updating) return; var mediaSegment = GetNextMediaSegment(); if (!mediaSegment) { // Error fetching the next media segment. mediaSource.endOfStream("network"); return; } // NOTE: If mediaSource.readyState == “ended”, this appendBuffer() call will // cause mediaSource.readyState to transition to "open". The web application // should be prepared to handle multiple “sourceopen” events. mediaSource.sourceBuffers[0].appendBuffer(mediaSegment); } function onSeeking(mediaSource, e) { var video = e.target; if (mediaSource.readyState == "open") { // Abort current segment append. mediaSource.sourceBuffers[0].abort(); } // Notify the media segment loading code to start fetching data at the // new playback position. SeekToMediaSegmentAt(video.currentTime); // Append a media segment from the new playback position. appendNextMediaSegment(mediaSource); } function onProgress(mediaSource, e) { appendNextMediaSegment(mediaSource); } ``` ```html ``` -------------------------------- ### Media Source Extensions Example Source: https://github.com/w3c/html-media/blob/main/media-source/media-source-wd.html Demonstrates the usage of the Media Source Extensions API in JavaScript for handling video playback. It covers opening a MediaSource, adding a SourceBuffer, appending initialization and media segments, and handling playback events like seeking and progress. ```JavaScript function onSourceOpen(videoTag, e) { var mediaSource = e.target; var sourceBuffer = mediaSource.addSourceBuffer('video/webm; codecs="vorbis,vp8"'); videoTag.addEventListener('seeking', onSeeking.bind(videoTag, mediaSource)); videoTag.addEventListener('progress', onProgress.bind(videoTag, mediaSource)); var initSegment = GetInitializationSegment(); if (initSegment == null) { // Error fetching the initialization segment. Signal end of stream with an error. mediaSource.endOfStream("network"); return; } // Append the initialization segment. var firstAppendHandler = function(e) { var sourceBuffer = e.target; sourceBuffer.removeEventListener('updateend', firstAppendHandler); // Append some initial media data. appendNextMediaSegment(mediaSource); }; sourceBuffer.addEventListener('updateend', firstAppendHandler); sourceBuffer.appendBuffer(initSegment); } function appendNextMediaSegment(mediaSource) { if (mediaSource.readyState == "ended") return; // If we have run out of stream data, then signal end of stream. if (!HaveMoreMediaSegments()) { mediaSource.endOfStream(); return; } // Make sure the previous append is not still pending. if (mediaSource.sourceBuffers[0].updating) return; var mediaSegment = GetNextMediaSegment(); if (!mediaSegment) { // Error fetching the next media segment. mediaSource.endOfStream("network"); return; } mediaSource.sourceBuffers[0].appendBuffer(mediaSegment); } function onSeeking(mediaSource, e) { var video = e.target; // Abort current segment append. mediaSource.sourceBuffers[0].abort(); // Notify the media segment loading code to start fetching data at the // new playback position. SeekToMediaSegmentAt(video.currentTime); // Append a media segment from the new playback position. appendNextMediaSegment(mediaSource); } function onProgress(mediaSource, e) { appendNextMediaSegment(mediaSource); } // Example setup: var video = document.getElementById('v'); var mediaSource = new MediaSource(); mediaSource.addEventListener('sourceopen', onSourceOpen.bind(this, video)); video.src = window.URL.createObjectURL(mediaSource); ``` -------------------------------- ### Select Key System and Handle Init Data (JavaScript) Source: https://github.com/w3c/html-media/blob/main/encrypted-media/encrypted-media-respec.html This example demonstrates selecting a supported key system and using initialization data from the 'encrypted' event to generate a license request. It handles potential errors by trying alternative key systems and manages pending session data until MediaKeys are ready. ```javascript var licenseUrl; var serverCertificate; // Returns a Promise. function createSupportedKeySystem() { var someSystemOptions = [ { contentType: "webm", audioCodecs: "vp8, vorbis" } ]; var clearKeyOptions = [ { contentType: "webm", audioCodecs: "vp8, vorbis" } ]; return navigator.requestMediaKeySystemAccess("com.example.somesystem", someSystemOptions).then( function(keySystemAccess) { licenseUrl = "https://license.example.com/getkey"; serverCertificate = new Uint8Array([ ... ]); return keySystemAccess.createMediaKeys(); } ).catch( function(error) { // Try the next key system. return navigator.requestMediaKeySystemAccess("org.w3.clearkey", clearKeyOptions).then( function(keySystemAccess) { licenseUrl = "https://license.example.com/clearkey/request"; return keySystemAccess.createMediaKeys(); } ); } ).catch( console.error.bind(console, "Unable to instantiate a key system supporting the required combinations") ); } function handleInitData(event) { var video = event.target; if (video.mediaKeysObject === undefined) { video.mediaKeysObject = null; // Prevent entering this path again. video.pendingSessionData = []; // Will store all initData until the MediaKeys is ready. createSupportedKeySystem().then( function(createdMediaKeys) { video.mediaKeysObject = createdMediaKeys; if (serverCertificate) createdMediaKeys.setServerCertificate(serverCertificate); for (var i = 0; i < video.pendingSessionData.length; i++) { var data = video.pendingSessionData[i]; makeNewRequest(video.mediaKeysObject, data.initDataType, data.initData); } video.pendingSessionData = []; return video.setMediaKeys(createdMediaKeys); } ).catch( console.error.bind(console, "Failed to create and initialize a MediaKeys object") ); } addSession(video, event.initDataType, event.initData); } function addSession(video, initDataType, initData) { if (video.mediaKeysObject) { makeNewRequest(video.mediaKeysObject, initDataType, initData); } else { video.pendingSessionData.push({initDataType: initDataType, initData: initData}); } } function makeNewRequest(mediaKeys, initDataType, initData) { var keySession = mediaKeys.createSession(); keySession.addEventListener("message", licenseRequestReady, false); keySession.generateRequest(initDataType, initData).catch( console.error.bind(console, "Unable to create or initialize key session") ); } function licenseRequestReady(event) { var request = event.message; var xmlhttp = new XMLHttpRequest(); xmlhttp.keySession = event.target; xmlhttp.open("POST", licenseUrl); xmlhttp.responseType = "arraybuffer"; xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4) { var license = new Uint8Array(xmlhttp.response); xmlhttp.keySession.update(license).catch( console.error.bind(console, "update() failed") ); } } xmlhttp.send(request); } ``` -------------------------------- ### WebM MIME-type Parameters and Examples Source: https://github.com/w3c/html-media/blob/main/media-source/webm-byte-stream-format.html Defines the parameters for MIME types used in Media Source Extensions, specifying supported codecs for audio/webm and video/webm. Includes examples of valid MIME type strings. ```APIDOC MIME-type Parameters: codecs: A comma separated list of codec IDs. Codec ID Support: vorbis: Valid with "audio/webm": true Valid with "video/webm": true opus: Valid with "audio/webm": true Valid with "video/webm": true vp8: Valid with "audio/webm": false Valid with "video/webm": true vp9: Valid with "audio/webm": false Valid with "video/webm": true Note: Implementations should support all listed codec IDs. Example MIME Types: audio/webm;codecs="vorbis" video/webm;codecs="vorbis" video/webm;codecs="vp8" video/webm;codecs="vp8,vorbis" video/webm;codecs="vp9,opus" ``` -------------------------------- ### Media Source Extensions Video Streaming Example Source: https://github.com/w3c/html-media/blob/main/media-source/media-source-cr.html Demonstrates how to use the Media Source Extensions API to stream video content. It includes functions for handling source opening, appending media segments, and responding to seeking and progress events. ```javascript function onSourceOpen(videoTag, e) { var mediaSource = e.target; if (mediaSource.sourceBuffers.length > 0) return; var sourceBuffer = mediaSource.addSourceBuffer('video/webm; codecs="vorbis,vp8"'); videoTag.addEventListener('seeking', onSeeking.bind(videoTag, mediaSource)); videoTag.addEventListener('progress', onProgress.bind(videoTag, mediaSource)); var initSegment = GetInitializationSegment(); if (initSegment == null) { // Error fetching the initialization segment. Signal end of stream with an error. mediaSource.endOfStream("network"); return; } // Append the initialization segment. var firstAppendHandler = function(e) { var sourceBuffer = e.target; sourceBuffer.removeEventListener('updateend', firstAppendHandler); // Append some initial media data. appendNextMediaSegment(mediaSource); }; sourceBuffer.addEventListener('updateend', firstAppendHandler); sourceBuffer.appendBuffer(initSegment); } function appendNextMediaSegment(mediaSource) { if (mediaSource.readyState == "closed") return; // If we have run out of stream data, then signal end of stream. if (!HaveMoreMediaSegments()) { mediaSource.endOfStream(); return; } // Make sure the previous append is not still pending. if (mediaSource.sourceBuffers[0].updating) return; var mediaSegment = GetNextMediaSegment(); if (!mediaSegment) { // Error fetching the next media segment. mediaSource.endOfStream("network"); return; } // NOTE: If mediaSource.readyState == “ended”, this appendBuffer() call will // cause mediaSource.readyState to transition to "open". The web application // should be prepared to handle multiple “sourceopen” events. mediaSource.sourceBuffers[0].appendBuffer(mediaSegment); } function onSeeking(mediaSource, e) { var video = e.target; if (mediaSource.readyState == "open") { // Abort current segment append. mediaSource.sourceBuffers[0].abort(); } // Notify the media segment loading code to start fetching data at the // new playback position. SeekToMediaSegmentAt(video.currentTime); // Append a media segment from the new playback position. appendNextMediaSegment(mediaSource); } function onProgress(mediaSource, e) { appendNextMediaSegment(mediaSource); } ``` ```html ``` -------------------------------- ### Complete EME Event Handling Example Source: https://github.com/w3c/html-media/blob/main/encrypted-media/encrypted-media-respec.html This JavaScript code demonstrates a complete example of handling various events for HTML media playback using the Encrypted Media Extensions (EME) API. It covers key system initialization, session creation, message exchange with a license server, and handling key status changes. ```javascript var licenseUrl; var serverCertificate; var mediaKeys; // See previous examples for implementations of these functions. // createSupportedKeySystem() additionally sets renewalUrl. function createSupportedKeySystem() { ... } function handleInitData(event) { ... } // This replaces the implementation in the previous example. function makeNewRequest(mediaKeys, initDataType, initData) { var keySession = mediaKeys.(); keySession.addEventListener("", handleMessage, false); keySession.addEventListener("", handleKeysChange, false); keySession..then( console.log.bind(console, "Session closed") ); keySession.(initDataType, initData).catch( console.error.bind(console, "Unable to create or initialize key session") ); } function handleMessageResponse(keySession, response) { var license = new Uint8Array(response); keySession.(license).catch( function(err) { console.error("update() failed: " + err); } ); } function sendMessage(type, message, keySession) { var url = licenseUrl; if (type == ) url = renewalUrl; xmlhttp = new XMLHttpRequest(); xmlhttp.keySession = keySession; xmlhttp.open("POST", url); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4) handleMessageResponse(xmlhttp.keySession, xmlhttp.response); } xmlhttp.send(message); } function handleMessage(event) { sendMessage(event., event., event.target); } function handleKeysChange(event) { event.target.().then( function(keyIdSequence) { // Process keyIdSequence and respond appropriately. } ).catch( console.error.bind(console, "Failed handling usable keys change") ); } function handleError(event) { // Report and do some bookkeeping with event.target. if necessary. } createSupportedKeySystem().then( function(createdMediaKeys) { mediaKeys = createdMediaKeys; var video = document.getElementById("v"); video.src = "foo.webm"; if (serverCertificate) mediaKeys.(serverCertificate); return video.(mediaKeys); } ).catch( console.error.bind(console, "Failed to create and initialize a MediaKeys object") ); ``` -------------------------------- ### Initialize Media Source and Add Source Buffer Source: https://github.com/w3c/html-media/blob/main/media-source/media-source-respec.html Example of initializing a MediaSource object and adding a SourceBuffer for a specific MIME type and codecs. It also demonstrates attaching an event listener for the 'seeking' event. ```javascript function onSourceOpen(videoTag, e) { var mediaSource = e.target; if (mediaSource.sourceBuffers.length > 0) return; var sourceBuffer = mediaSource.addSourceBuffer('video/webm; codecs="vorbis,vp8"'); videoTag.addEventListener('seeking', onSeeking.bind(videoTag, mediaSource)); } ``` -------------------------------- ### JavaScript EME MediaKeys and Key Session Setup (Clear Key) Source: https://github.com/w3c/html-media/blob/main/encrypted-media/encrypted-media-respec.html Demonstrates setting up Encrypted Media Extensions (EME) using JavaScript for media playback with a Clear Key license. It shows how to request a Key System, create MediaKeys, create a MediaKeySession, and handle license updates. ```javascript function load() { var video = document.getElementById("video"); if (!video.) { navigator.requestMediaKeySystemAccess("org.w3.clearkey", [{ initDataType: "webm" }]).then( function(keySystemAccess) { var promise = keySystemAccess.createMediaKeys(); promise.catch( console.error.bind(console, "Unable to create MediaKeys") ); promise.then( function(createdMediaKeys) { return video.setMediaKeys(createdMediaKeys); } ).catch( console.error.bind(console, "Unable to set MediaKeys") ); promise.then( function(createdMediaKeys) { var initData = new Uint8Array([ ... ]); var keySession = createdMediaKeys.createSession("video", initData); keySession.addEventListener("message", handleMessage, false); return keySession.generateRequest("webm", initData); } ).catch( console.error.bind(console, "Unable to create or initialize key session") ); } ); } } function handleMessage(event) { var keySession = event.target; var license = new Uint8Array([ ... ]); keySession.update(license).catch( console.error.bind(console, "update() failed") ); } ``` -------------------------------- ### Pre-initialize MediaKeys Before Loading Media (JavaScript) Source: https://github.com/w3c/html-media/blob/main/encrypted-media/encrypted-media-respec.html This example shows how to create and initialize a MediaKeys object before setting the video source, simplifying the handling of encrypted events. This approach ensures MediaKeys are available when the 'encrypted' event fires, avoiding the need to buffer initialization data. ```javascript var licenseUrl; var serverCertificate; var mediaKeys; // See the previous example for implementations of these functions. // function createSupportedKeySystem() { ... } // function makeNewRequest(mediaKeys, initDataType, initData) { ... } // function licenseRequestReady(event) { ... } function handleInitData(event) { makeNewRequest(mediaKeys, event.initDataType, event.initData); } createSupportedKeySystem().then( function(createdMediaKeys) { mediaKeys = createdMediaKeys; var video = document.getElementById("v"); video.src = "foo.webm"; if (serverCertificate) mediaKeys.setServerCertificate(serverCertificate); return video.setMediaKeys(mediaKeys); } ).catch( console.error.bind(console, "Failed to create and initialize a MediaKeys object") ); ``` -------------------------------- ### Handle Encrypted Media Events with KeySession (JavaScript) Source: https://github.com/w3c/html-media/blob/main/encrypted-media/encrypted-media-wd.html This example illustrates a more comprehensive approach to handling encrypted media events using MediaKeys and KeySession. It covers creating a session, listening for message and keyschange events, sending license requests via XMLHttpRequest, and updating the session with the license response. It also includes error handling and session closure logging. ```javascript var keySystem; var licenseUrl; var serverCertificate; var mediaKeys; // See previous examples for implementations of these functions. function selectKeySystem() { /* ... */ } function handleInitData(event) { /* ... */ } // This replaces the implementation in the previous example. function createSession(mediaKeys, initDataType, initData) { mediaKeys.createSession(initDataType, initData).then( function(keySession) { keySession.addEventListener("message", handleMessage, false); keySession.addEventListener("keyschange", handleKeysChange, false); keySession.closed.then( console.log.bind(console, "Session closed") ); } ).catch( console.error.bind(console, "Unable to create or initialize key session") ); } function handleMessageResponse(keySession, response) { var license = new Uint8Array(response); keySession.update(license).catch( function(err) { console.error("update() failed: " + err); } ); } function sendMessage(message, keySession) { xmlhttp = new XMLHttpRequest(); xmlhttp.keySession = keySession; xmlhttp.open("POST", licenseUrl); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4) handleMessageResponse(xmlhttp.keySession, xmlhttp.response); } xmlhttp.send(message); } function handleMessage(event) { sendMessage(event.message, event.target); } function handleKeysChange(event) { event.target.getUsableKeyIds().then( function(keyIdSequence) { // Process keyIdSequence and respond appropriately. } ).catch( console.error.bind(console, "Failed handling usable keys change") ); } function handleError(event) { // Report and do some bookkeeping with event.target.sessionId if necessary. } selectKeySystem(); MediaKeys.create(keySystem).then( function(createdMediaKeys) { mediaKeys = createdMediaKeys; var video = document.getElementById("v"); video.src = "foo.webm"; if (serverCertificate) mediaKeys.setServerCertificate(serverCertificate); return video.setMediaKeys(mediaKeys); } ).catch( console.error.bind(console, "Unable to create or use new MediaKeys") ); ``` -------------------------------- ### JavaScript EME Example: Load Media with Clear Key Source: https://github.com/w3c/html-media/blob/main/encrypted-media/encrypted-media.html Demonstrates how to load and play encrypted media using the Encrypted Media Extensions (EME) API with the Clear Key content decryption module. It covers requesting access to a key system, creating media keys, setting them on a video element, and generating/updating a key session. ```javascript function load() { var video = document.getElementById("video"); if (!video.mediaKeys) { navigator.requestMediaKeySystemAccess("org.w3.clearkey", [ { initDataTypes: ["webm"], audioCapabilities: [ { contentType: "audio/webm; codecs=\"opus\"" } ], videoCapabilities: [ { contentType: "video/webm; codecs=\"vp8\"" } ] } ]).then( function(keySystemAccess) { var promise = keySystemAccess.createMediaKeys(); promise.catch( console.error.bind(console, "Unable to create MediaKeys") ); promise.then( function(createdMediaKeys) { return video.setMediaKeys(createdMediaKeys); } ).catch( console.error.bind(console, "Unable to set MediaKeys") ); promise.then( function(createdMediaKeys) { var initData = new Uint8Array([ ... ]); // Placeholder for actual init data var keySession = createdMediaKeys.createSession(); keySession.addEventListener("message", handleMessage, false); return keySession.generateRequest("webm", initData); } ).catch( console.error.bind(console, "Unable to create or initialize key session") ); } ); } } function handleMessage(event) { var keySession = event.target; var license = new Uint8Array([ ... ]); // Placeholder for actual license data keySession.update(license).catch( console.error.bind(console, "update() failed") ); } // Call load when the page is ready // window.onload = load; // Or similar event listener ``` -------------------------------- ### WebM Initialization Segment Structure Source: https://github.com/w3c/html-media/blob/main/media-source/media-source-fpwd.html Defines the required elements and their order within a WebM initialization segment. It must start with an EBML Header and a Segment header, followed by Segment Information and Tracks elements. The size of the Segment header is flexible, allowing for unknown or large sizes. ```APIDOC WebMInitializationSegment: - EBML Header - Segment Header - Size: Unknown or large enough to contain subsequent elements - Segment Information - Tracks - Ignored Elements (other than EBML Header or Cluster before/between/after Segment Information and Tracks) ``` -------------------------------- ### Remove Media Segments Start with RAP Requirement Source: https://github.com/w3c/html-media/blob/main/media-source/media-source-wd.html Removes the requirement that media segments must start with a Random Access Point (RAP). This offers more flexibility in how media segments can be structured and appended. ```APIDOC Media Segment Requirements: - Removed: Media segments must start with a Random Access Point (RAP). ``` -------------------------------- ### JavaScript for Media Key System and Initialization Data Handling Source: https://github.com/w3c/html-media/blob/main/encrypted-media/encrypted-media-wd.html This script demonstrates how to select a supported key system using `MediaKeys.isTypeSupported()` and process initialization data from the 'encrypted' event. It handles the creation of `MediaKeys` objects, setting server certificates, creating key sessions, and managing license requests via `XMLHttpRequest`. ```javascript var keySystem; var licenseUrl; var serverCertificate; function selectKeySystem() { if (MediaKeys.isTypeSupported("com.example.somesystem", "webm", "video/webm; codecs='vp8, vorbis'")) { licenseUrl = "https://license.example.com/getkey"; // OR "https://example." keySystem = "com.example.somesystem"; serverCertificate = new Uint8Array([ ... ]); } else if (MediaKeys.isTypeSupported("com.foobar", "webm", "video/webm; codecs='vp8, vorbis'")) { licenseUrl = "https://license.foobar.com/request"; keySystem = "com.foobar"; } else { throw "Key System not supported"; } } function handleInitData(event) { var video = event.target; if (video.mediaKeysObject === undefined) { selectKeySystem(); video.mediaKeysObject = null; // Prevent entering this path again. video.pendingSessionData = []; // Will store all initData until the MediaKeys is ready. MediaKeys.create(keySystem).then( function(createdMediaKeys) { video.mediaKeysObject = createdMediaKeys; if (serverCertificate) createdMediaKeys.setServerCertificate(serverCertificate); for (var i = 0; i < video.pendingSessionData.length; i++) { var data = video.pendingSessionData[i]; createSession(video.mediaKeysObject, data.initDataType, data.initData); } video.pendingSessionData = []; return video.setMediaKeys(createdMediaKeys); } ).catch( console.error.bind(console, "Unable to create or use new MediaKeys") ); } addSession(video, event.initDataType, event.initData); } function addSession(video, initDataType, initData) { if (video.mediaKeysObject) { createSession(video.mediaKeysObject, initDataType, initData); } else { video.pendingSessionData.push({initDataType: initDataType, initData: initData}); } } function createSession(mediaKeys, initDataType, initData) { mediaKeys.createSession(initDataType, initData).then( function(keySession) { keySession.addEventListener("message", licenseRequestReady, false); } ).catch( console.error.bind(console, "Unable to create or initialize key session") ); } function licenseRequestReady(event) { var request = event.message; var xmlhttp = new XMLHttpRequest(); xmlhttp.keySession = event.target; xmlhttp.open("POST", licenseUrl); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4) { var license = new Uint8Array(xmlhttp.response); xmlhttp.keySession.update(license).catch( console.error.bind(console, "update() failed") ); } } xmlhttp.send(request); } ``` -------------------------------- ### Clear Key Media Initialization JavaScript Source: https://github.com/w3c/html-media/blob/main/encrypted-media/encrypted-media-wd.html Initializes MediaKeys for the Clear Key CDM and sets up a key session for media playback. It handles the asynchronous creation of MediaKeys and sessions, attaching event listeners for license message handling. ```javascript function load() { var video = document.getElementById("video"); if (!video.mediaKeys) { var promise = MediaKeys.create("org.w3.clearkey"); promise.catch( console.error.bind(console, "Unable to create MediaKeys") ); promise.then( function(createdMediaKeys) { return video.setMediaKeys(createdMediaKeys); } ).catch( console.error.bind(console, "Unable to set MediaKeys") ); promise.then( function(createdMediaKeys) { var initData = new Uint8Array([ ... ]); return createdMediaKeys.createSession("webm", initData); } ).then( function(keySession) { keySession.addEventListener("message", handleMessage, false); } ).catch( console.error.bind(console, "Unable to create key session") ); } } function handleMessage(event) { var keySession = event.target; var license = new Uint8Array([ ... ]); keySession.update(license).catch( console.error.bind(console, "update() failed") ); } ``` ```html ``` -------------------------------- ### HTML Media Element Setup Source: https://github.com/w3c/html-media/blob/main/encrypted-media/encrypted-media-respec.html Basic HTML structure to embed a video element for media playback, including an onload attribute to trigger the JavaScript initialization. ```html ``` -------------------------------- ### HTMLVideoElement: Get Playback Quality Source: https://github.com/w3c/html-media/blob/main/media-source/media-source-lc.html Retrieves current video playback quality metrics, including frame counts and delays. The method returns a VideoPlaybackQuality object populated with internal counters. ```APIDOC partial interface HTMLVideoElement { [`VideoPlaybackQuality`](#idl-def-VideoPlaybackQuality) [getVideoPlaybackQuality](#widl-HTMLVideoElement-getVideoPlaybackQuality-VideoPlaybackQuality) (); }; Provides the current the playback quality metrics. _No parameters._ _Return type:_ ``[`VideoPlaybackQuality`](#idl-def-VideoPlaybackQuality)`` When this method is invoked, the user agent must run the following steps: 1. Let playbackQuality be a new instance of [`VideoPlaybackQuality`](#idl-def-VideoPlaybackQuality). 2. Set playbackQuality.`[creationTime](#widl-VideoPlaybackQuality-creationTime)` to the value returned by a call to [Performance.now()](http://www.w3.org/TR/hr-time/#dom-performance-now). 3. Set playbackQuality.`[totalVideoFrames](#widl-VideoPlaybackQuality-totalVideoFrames)` to the current value of the [total video frame count](#total-video-frame-count). 4. Set playbackQuality.`[droppedVideoFrames](#widl-VideoPlaybackQuality-droppedVideoFrames)` to the current value of the [dropped video frame count](#dropped-video-frame-count). 5. Set playbackQuality.`[corruptedVideoFrames](#widl-VideoPlaybackQuality-corruptedVideoFrames)` to the current value of the [corrupted video frame count](#corrupted-video-frame-count). 6. Set playbackQuality.`[totalFrameDelay](#widl-VideoPlaybackQuality-totalFrameDelay)` to the current value of the [displayed frame delay sum](#displayed-frame-delay-sum). 7. Return playbackQuality. ``` -------------------------------- ### Append Window Attributes Source: https://github.com/w3c/html-media/blob/main/media-source/media-source-respec.html Manages the append window for media segments, defining the valid time range for appending data. Includes start and end time attributes. ```APIDOC attribute double appendWindowStart The start of the append window. This attribute is initially set to the minimum possible timestamp offset. On getting, returns the initial or last successfully set value. On setting, throws an exception if the object is removed, media is playing, or the value is out of range [0, positive Infinity). attribute unrestricted double appendWindowEnd The end of the append window. This attribute is initially set to positive Infinity. On getting, returns the initial or last successfully set value. On setting, throws an exception if the object is removed, media is playing, the value is NaN, or less than or equal to appendWindowStart. ``` -------------------------------- ### SourceBuffer Initialization and State Changes Source: https://github.com/w3c/html-media/blob/main/media-source/media-source-cr.html Outlines the process for managing the 'first initialization segment flag' for SourceBuffers and how it affects the HTMLMediaElement's readyState. It details the transition from HAVE_NOTHING to HAVE_METADATA. ```APIDOC SourceBuffer.first_initialization_segment_flag - Flag indicating if the first initialization segment has been processed. HTMLMediaElement.readyState - Transitions from HAVE_NOTHING to HAVE_METADATA based on SourceBuffer initialization. - If `readyState` is HAVE_NOTHING: - If any `SourceBuffer` in `sourceBuffers` has `first_initialization_segment_flag` set to false, abort. - Set `readyState` to HAVE_METADATA. - Queue a 'loadedmetadata' event on the media element. Event: loadedmetadata (on HTMLMediaElement) - Fires when the media's metadata has been loaded. ```