From 46b1cfa552ba860cbce80f3b9e26acf337589793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Velad=20Galv=C3=A1n?= Date: Wed, 5 May 2021 19:15:03 +0200 Subject: [PATCH] feat: Progress toward FairPlay DRM w/ MSE (#3347) Related to: #3346 --- demo/main.js | 2 +- lib/media/drm_engine.js | 19 ++++++++++++ lib/player.js | 55 +++++++++++++++++++++++------------ test/media/drm_engine_unit.js | 20 +++++++++++++ 4 files changed, 76 insertions(+), 20 deletions(-) diff --git a/demo/main.js b/demo/main.js index e23cd6cb37..9dae383dac 100644 --- a/demo/main.js +++ b/demo/main.js @@ -1739,7 +1739,7 @@ shakaDemo.Main = class { shakaDemo.Main.commonDrmSystems = [ 'com.widevine.alpha', 'com.microsoft.playready', - 'com.apple.fps.1_0', + 'com.apple.fps', 'com.adobe.primetime', 'org.w3.clearkey', ]; diff --git a/lib/media/drm_engine.js b/lib/media/drm_engine.js index e34314749b..4c93ff8842 100644 --- a/lib/media/drm_engine.js +++ b/lib/media/drm_engine.js @@ -630,6 +630,17 @@ shaka.media.DrmEngine = class { return false; } + /** + * @param {?string} keySystem + * @return {boolean} */ + static isFairPlayKeySystem(keySystem) { + if (keySystem) { + return !!keySystem.match(/^com\.apple\.fps/); + } + + return false; + } + /** * Check if DrmEngine (as initialized) will likely be able to support the * given content type. @@ -827,6 +838,14 @@ shaka.media.DrmEngine = class { ]; } + // FairPlay does not support distinctiveIdentifier, persistentState + // and also only supports temporary sessions. + if (shaka.media.DrmEngine.isFairPlayKeySystem(info.keySystem)) { + config.distinctiveIdentifier = 'not-allowed'; + config.persistentState = 'not-allowed'; + config.sessionTypes = ['temporary']; + } + if (info.distinctiveIdentifierRequired) { config.distinctiveIdentifier = 'required'; } diff --git a/lib/player.js b/lib/player.js index ebcb6d13d4..4eef2b062e 100644 --- a/lib/player.js +++ b/lib/player.js @@ -1142,24 +1142,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget { if (!mimeType) { // Try using the uri extension. const extension = shaka.media.ManifestParser.getExtension(uri); - mimeType = { - 'mp4': 'video/mp4', - 'm4v': 'video/mp4', - 'm4a': 'audio/mp4', - 'webm': 'video/webm', - 'weba': 'audio/webm', - 'mkv': 'video/webm', // Chromium browsers supports it. - 'ts': 'video/mp2t', - 'ogv': 'video/ogg', - 'ogg': 'audio/ogg', - 'mpg': 'video/mpeg', - 'mpeg': 'video/mpeg', - 'm3u8': 'application/x-mpegurl', - 'mp3': 'audio/mpeg', - 'aac': 'audio/aac', - 'flac': 'audio/flac', - 'wav': 'audio/wav', - }[extension]; + mimeType = shaka.Player.SRC_EQUAL_EXTENSIONS_TO_MIME_TYPES_[extension]; } // TODO: The load graph system has a design limitation that requires routing @@ -2000,6 +1983,16 @@ shaka.Player = class extends shaka.util.FakeEventTarget { this.drmEngine_.configure(this.config_.drm); + const uri = has.uri || ''; + const extension = shaka.media.ManifestParser.getExtension(uri); + let mimeType = shaka.Player.SRC_EQUAL_EXTENSIONS_TO_MIME_TYPES_[extension]; + if (mimeType == 'application/x-mpegurl' && shaka.util.Platform.isApple()) { + mimeType = 'application/vnd.apple.mpegurl'; + } + if (!mimeType) { + mimeType = 'video/mp4'; + } + // TODO: Instead of feeding DrmEngine with Variants, we should refactor // DrmEngine so that it takes a minimal config derived from Variants. In // cases like this one or in removal of stored content, the details are @@ -2020,7 +2013,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget { createSegmentIndex: () => Promise.resolve(), segmentIndex: null, mimeType: wants.mimeType ? - shaka.util.MimeUtils.getBasicType(wants.mimeType) : 'video/mp4', + shaka.util.MimeUtils.getBasicType(wants.mimeType) : mimeType, codecs: wants.mimeType ? shaka.util.MimeUtils.getCodecs(wants.mimeType) : '', encrypted: true, @@ -6074,6 +6067,30 @@ shaka.Player.supportPlugins_ = {}; shaka.Player.adManagerFactory_ = null; +/** + * @const {!Object.} + * @private + */ +shaka.Player.SRC_EQUAL_EXTENSIONS_TO_MIME_TYPES_ = { + 'mp4': 'video/mp4', + 'm4v': 'video/mp4', + 'm4a': 'audio/mp4', + 'webm': 'video/webm', + 'weba': 'audio/webm', + 'mkv': 'video/webm', // Chromium browsers supports it. + 'ts': 'video/mp2t', + 'ogv': 'video/ogg', + 'ogg': 'audio/ogg', + 'mpg': 'video/mpeg', + 'mpeg': 'video/mpeg', + 'm3u8': 'application/x-mpegurl', + 'mp3': 'audio/mpeg', + 'aac': 'audio/aac', + 'flac': 'audio/flac', + 'wav': 'audio/wav', +}; + + /** * @const {string} */ diff --git a/test/media/drm_engine_unit.js b/test/media/drm_engine_unit.js index a4dbc65217..3cfd4de9ed 100644 --- a/test/media/drm_engine_unit.js +++ b/test/media/drm_engine_unit.js @@ -2250,6 +2250,26 @@ function testDrmEngine(useMediaCapabilities) { }); }); + describe('isFairPlayKeySystem', () => { + it('should return true for FairPlay', () => { + expect(shaka.media.DrmEngine.isFairPlayKeySystem( + 'com.apple.fps')).toBe(true); + expect(shaka.media.DrmEngine.isFairPlayKeySystem( + 'com.apple.fps.1_0')).toBe(true); + expect(shaka.media.DrmEngine.isFairPlayKeySystem( + 'com.apple.fps.2_0')).toBe(true); + expect(shaka.media.DrmEngine.isFairPlayKeySystem( + 'com.apple.fps.3_0')).toBe(true); + }); + + it('should return false for non-FairPlay key systems', () => { + expect(shaka.media.DrmEngine.isFairPlayKeySystem( + 'com.widevine.alpha')).toBe(false); + expect(shaka.media.DrmEngine.isFairPlayKeySystem( + 'com.abc.playready')).toBe(false); + }); + }); + describe('getDrmInfo', () => { it('includes correct info', async () => { // Leave only one drmInfo