From bc0872a73ec824a043c4d51a408dbb79025c4bfc Mon Sep 17 00:00:00 2001 From: Brandon Casey <2381475+brandonocasey@users.noreply.github.com> Date: Tue, 29 Sep 2020 16:27:56 -0400 Subject: [PATCH] feat: add CODECS attribute to subtitle playlists if it exists (#106) --- index.html | 4 +- src/toM3u8.js | 16 +- test/index.test.js | 11 ++ test/manifests/vtt_codecs.js | 348 ++++++++++++++++++++++++++++++++++ test/manifests/vtt_codecs.mpd | 60 ++++++ 5 files changed, 432 insertions(+), 7 deletions(-) create mode 100644 test/manifests/vtt_codecs.js create mode 100644 test/manifests/vtt_codecs.mpd diff --git a/index.html b/index.html index a65c645e..e7522bcd 100644 --- a/index.html +++ b/index.html @@ -7,8 +7,8 @@

Open dev tools to try it out

diff --git a/src/toM3u8.js b/src/toM3u8.js index 3042e989..05d7aee6 100644 --- a/src/toM3u8.js +++ b/src/toM3u8.js @@ -105,12 +105,18 @@ export const formatVttPlaylist = ({ attributes, segments }) => { // targetDuration should be the same duration as the only segment attributes.duration = attributes.sourceDuration; } + + const m3u8Attributes = { + NAME: attributes.id, + BANDWIDTH: attributes.bandwidth, + ['PROGRAM-ID']: 1 + }; + + if (attributes.codecs) { + m3u8Attributes.CODECS = attributes.codecs; + } return { - attributes: { - NAME: attributes.id, - BANDWIDTH: attributes.bandwidth, - ['PROGRAM-ID']: 1 - }, + attributes: m3u8Attributes, uri: '', endList: (attributes.type || 'static') === 'static', timeline: attributes.periodIndex, diff --git a/test/index.test.js b/test/index.test.js index 58598815..d3c8c37e 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -1,7 +1,10 @@ import { parse, VERSION } from '../src'; import QUnit from 'qunit'; +QUnit.dump.maxDepth = Infinity; + // manifests +import vttCodecsTemplate from './manifests/vtt_codecs.mpd'; import maatVttSegmentTemplate from './manifests/maat_vtt_segmentTemplate.mpd'; import segmentBaseTemplate from './manifests/segmentBase.mpd'; import segmentListTemplate from './manifests/segmentList.mpd'; @@ -31,6 +34,10 @@ import { parsedManifest as locationsManifest } from './manifests/locations.js'; +import { + parsedManifest as vttCodecsManifest +} from './manifests/vtt_codecs.js'; + QUnit.module('mpd-parser'); QUnit.test('has VERSION', function(assert) { @@ -69,6 +76,10 @@ QUnit.test('has parse', function(assert) { name: 'locations', input: locationsTemplate, expected: locationsManifest +}, { + name: 'vtt_codecs', + input: vttCodecsTemplate, + expected: vttCodecsManifest }].forEach(({ name, input, expected }) => { QUnit.test(`${name} test manifest`, function(assert) { const actual = parse(input); diff --git a/test/manifests/vtt_codecs.js b/test/manifests/vtt_codecs.js new file mode 100644 index 00000000..04782e36 --- /dev/null +++ b/test/manifests/vtt_codecs.js @@ -0,0 +1,348 @@ +export const parsedManifest = { + allowCache: true, + discontinuityStarts: [], + duration: 6, + endList: true, + mediaGroups: { + AUDIO: { + audio: { + ['en (main)']: { + autoselect: true, + default: true, + language: 'en', + playlists: [{ + attributes: { + BANDWIDTH: 125000, + CODECS: 'mp4a.40.2', + NAME: '125000', + ['PROGRAM-ID']: 1 + }, + contentProtection: { + 'com.widevine.alpha': { + attributes: { + schemeIdUri: 'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed' + }, + pssh: new Uint8Array([181, 235, 45]) + } + }, + endList: true, + mediaSequence: 0, + targetDuration: 1.984, + resolvedUri: '', + segments: [{ + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/init.m4f', + uri: '125000/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/0.m4f', + timeline: 0, + uri: '125000/0.m4f', + number: 0 + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/init.m4f', + uri: '125000/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/1.m4f', + timeline: 0, + uri: '125000/1.m4f', + number: 1 + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/init.m4f', + uri: '125000/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/2.m4f', + timeline: 0, + uri: '125000/2.m4f', + number: 2 + }, { + duration: 0.04800000000000004, + map: { + resolvedUri: 'https://www.example.com/125000/init.m4f', + uri: '125000/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/3.m4f', + timeline: 0, + uri: '125000/3.m4f', + number: 3 + }], + timeline: 0, + uri: '' + }], + uri: '' + }, + ['es']: { + autoselect: true, + default: false, + language: 'es', + playlists: [{ + attributes: { + BANDWIDTH: 125000, + CODECS: 'mp4a.40.2', + NAME: '125000', + ['PROGRAM-ID']: 1 + }, + contentProtection: { + 'com.widevine.alpha': { + attributes: { + schemeIdUri: 'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed' + }, + pssh: new Uint8Array([181, 235, 45]) + } + }, + endList: true, + targetDuration: 1.984, + mediaSequence: 0, + resolvedUri: '', + segments: [{ + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/0.m4f', + timeline: 0, + uri: '125000/es/0.m4f', + number: 0 + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/1.m4f', + timeline: 0, + uri: '125000/es/1.m4f', + number: 1 + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/2.m4f', + timeline: 0, + uri: '125000/es/2.m4f', + number: 2 + }, { + duration: 0.04800000000000004, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/3.m4f', + timeline: 0, + uri: '125000/es/3.m4f', + number: 3 + }], + timeline: 0, + uri: '' + }], + uri: '' + } + } + }, + ['CLOSED-CAPTIONS']: {}, + SUBTITLES: { + subs: { + en: { + autoselect: false, + default: false, + language: 'en', + playlists: [{ + attributes: { + BANDWIDTH: 256, + NAME: 'en', + CODECS: 'stpp.ttml.im1t', + ['PROGRAM-ID']: 1 + }, + mediaSequence: 0, + endList: true, + targetDuration: 6, + resolvedUri: 'https://example.com/en.dash', + segments: [{ + duration: 6, + resolvedUri: 'https://example.com/en.dash', + timeline: 0, + uri: 'https://example.com/en.dash', + number: 0 + }], + timeline: 0, + uri: '' + }], + uri: '' + }, + es: { + autoselect: false, + default: false, + language: 'es', + playlists: [{ + attributes: { + BANDWIDTH: 256, + NAME: 'es', + ['PROGRAM-ID']: 1 + }, + endList: true, + targetDuration: 6, + mediaSequence: 0, + resolvedUri: 'https://example.com/es.vtt', + segments: [{ + duration: 6, + resolvedUri: 'https://example.com/es.vtt', + timeline: 0, + uri: 'https://example.com/es.vtt', + number: 0 + }], + timeline: 0, + uri: '' + }], + uri: '' + } + } + }, + VIDEO: {} + }, + playlists: [{ + attributes: { + AUDIO: 'audio', + SUBTITLES: 'subs', + BANDWIDTH: 449000, + CODECS: 'avc1.420015', + NAME: '482', + ['PROGRAM-ID']: 1, + RESOLUTION: { + height: 270, + width: 482 + } + }, + contentProtection: { + 'com.widevine.alpha': { + attributes: { + schemeIdUri: 'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed' + }, + pssh: new Uint8Array([181, 235, 45]) + } + }, + endList: true, + targetDuration: 1.9185833333333333, + mediaSequence: 0, + resolvedUri: '', + segments: [{ + duration: 1.9185833333333333, + map: { + resolvedUri: 'https://www.example.com/482/init.m4f', + uri: '482/init.m4f' + }, + resolvedUri: 'https://www.example.com/482/0.m4f', + timeline: 0, + uri: '482/0.m4f', + number: 0 + }, { + duration: 1.9185833333333333, + map: { + resolvedUri: 'https://www.example.com/482/init.m4f', + uri: '482/init.m4f' + }, + resolvedUri: 'https://www.example.com/482/1.m4f', + timeline: 0, + uri: '482/1.m4f', + number: 1 + }, { + duration: 1.9185833333333333, + map: { + resolvedUri: 'https://www.example.com/482/init.m4f', + uri: '482/init.m4f' + }, + resolvedUri: 'https://www.example.com/482/2.m4f', + timeline: 0, + uri: '482/2.m4f', + number: 2 + }, { + duration: 0.24425000000000008, + map: { + resolvedUri: 'https://www.example.com/482/init.m4f', + uri: '482/init.m4f' + }, + resolvedUri: 'https://www.example.com/482/3.m4f', + timeline: 0, + uri: '482/3.m4f', + number: 3 + }], + timeline: 0, + uri: '' + }, { + attributes: { + AUDIO: 'audio', + SUBTITLES: 'subs', + BANDWIDTH: 3971000, + CODECS: 'avc1.64001e', + NAME: '720', + ['PROGRAM-ID']: 1, + RESOLUTION: { + height: 404, + width: 720 + } + }, + contentProtection: { + 'com.widevine.alpha': { + attributes: { + schemeIdUri: 'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed' + }, + pssh: new Uint8Array([181, 235, 45]) + } + }, + endList: true, + targetDuration: 1.9185833333333333, + mediaSequence: 0, + resolvedUri: '', + segments: [{ + duration: 1.9185833333333333, + map: { + resolvedUri: 'https://www.example.com/720/init.m4f', + uri: '720/init.m4f' + }, + resolvedUri: 'https://www.example.com/720/0.m4f', + timeline: 0, + uri: '720/0.m4f', + number: 0 + }, { + duration: 1.9185833333333333, + map: { + resolvedUri: 'https://www.example.com/720/init.m4f', + uri: '720/init.m4f' + }, + resolvedUri: 'https://www.example.com/720/1.m4f', + timeline: 0, + uri: '720/1.m4f', + number: 1 + }, { + duration: 1.9185833333333333, + map: { + resolvedUri: 'https://www.example.com/720/init.m4f', + uri: '720/init.m4f' + }, + resolvedUri: 'https://www.example.com/720/2.m4f', + timeline: 0, + uri: '720/2.m4f', + number: 2 + }, { + duration: 0.24425000000000008, + map: { + resolvedUri: 'https://www.example.com/720/init.m4f', + uri: '720/init.m4f' + }, + resolvedUri: 'https://www.example.com/720/3.m4f', + timeline: 0, + uri: '720/3.m4f', + number: 3 + }], + timeline: 0, + uri: '' + }], + segments: [], + uri: '' +}; diff --git a/test/manifests/vtt_codecs.mpd b/test/manifests/vtt_codecs.mpd new file mode 100644 index 00000000..c3ab2669 --- /dev/null +++ b/test/manifests/vtt_codecs.mpd @@ -0,0 +1,60 @@ + + + https://www.example.com/base + + + + + + test + + + + + + + + + + + + + + test + + + + + + + + + + + + + + test + + + + + + + + + + + + + https://example.com/en.dash + + + + + https://example.com/es.vtt + + + + +