diff --git a/src/inheritAttributes.js b/src/inheritAttributes.js
index 3085f2f9..e534f75b 100644
--- a/src/inheritAttributes.js
+++ b/src/inheritAttributes.js
@@ -202,7 +202,8 @@ export const parseCaptionServiceMetadata = (service) => {
const values = service.value.split(';');
return values.map((value) => {
- let channel; let language;
+ let channel;
+ let language;
// default language to value
language = value;
@@ -215,6 +216,68 @@ export const parseCaptionServiceMetadata = (service) => {
return {channel, language};
});
+ } else if (service.schemeIdUri === 'urn:scte:dash:cc:cea-708:2015') {
+ const values = service.value.split(';');
+
+ return values.map((value) => {
+ const flags = {
+ // service or channel number 1-63
+ 'channel': undefined,
+
+ // language is a 3ALPHA per ISO 639.2/B
+ // field is required
+ 'language': undefined,
+
+ // BIT 1/0 or ?
+ // default value is 1, meaning 16:9 aspect ratio, 0 is 4:3, ? is unknown
+ 'aspectRatio': 1,
+
+ // BIT 1/0
+ // easy reader flag indicated the text is tailed to the needs of beginning readers
+ // default 0, or off
+ 'easyReader': 0,
+
+ // BIT 1/0
+ // If 3d metadata is present (CEA-708.1) then 1
+ // default 0
+ '3D': 0
+ };
+
+ if (/=/.test(value)) {
+
+ const [channel, opts = ''] = value.split('=');
+
+ flags.channel = channel;
+ flags.language = value;
+
+ opts.split(',').forEach((opt) => {
+ const [name, val] = opt.split(':');
+
+ if (name === 'lang') {
+ flags.language = val;
+
+ // er for easyReadery
+ } else if (name === 'er') {
+ flags.easyReader = Number(val);
+
+ // war for wide aspect ratio
+ } else if (name === 'war') {
+ flags.aspectRatio = Number(val);
+
+ } else if (name === '3D') {
+ flags['3D'] = Number(val);
+ }
+ });
+ } else {
+ flags.language = value;
+ }
+
+ if (flags.channel) {
+ flags.channel = 'SERVICE' + flags.channel;
+ }
+
+ return flags;
+ });
}
};
diff --git a/src/toM3u8.js b/src/toM3u8.js
index 00cfe5fb..970b0ae9 100644
--- a/src/toM3u8.js
+++ b/src/toM3u8.js
@@ -206,13 +206,29 @@ const organizeCaptionServices = (captionServices) => captionServices.reduce((svc
return svcObj;
}
- svc.forEach(({channel, language}) => {
+ svc.forEach((service) => {
+ const {
+ channel,
+ language
+ } = service;
+
svcObj[language] = {
autoselect: false,
default: false,
instreamId: channel,
language
};
+
+ if (service.hasOwnProperty('aspectRatio')) {
+ svcObj[language].aspectRatio = service.aspectRatio;
+ }
+ if (service.hasOwnProperty('easyReader')) {
+ svcObj[language].easyReader = service.easyReader;
+ }
+ if (service.hasOwnProperty('3D')) {
+ svcObj[language]['3D'] = service['3D'];
+ }
+
});
return svcObj;
diff --git a/test/index.test.js b/test/index.test.js
index 64324789..694405b1 100644
--- a/test/index.test.js
+++ b/test/index.test.js
@@ -9,6 +9,7 @@ import maatVttSegmentTemplate from './manifests/maat_vtt_segmentTemplate.mpd';
import segmentBaseTemplate from './manifests/segmentBase.mpd';
import segmentListTemplate from './manifests/segmentList.mpd';
import cc608CaptionsTemplate from './manifests/608-captions.mpd';
+import cc708CaptionsTemplate from './manifests/708-captions.mpd';
import locationTemplate from './manifests/location.mpd';
import locationsTemplate from './manifests/locations.mpd';
import multiperiod from './manifests/multiperiod.mpd';
@@ -27,6 +28,9 @@ import {
import {
parsedManifest as cc608CaptionsManifest
} from './manifests/608-captions.js';
+import {
+ parsedManifest as cc708CaptionsManifest
+} from './manifests/708-captions.js';
import {
parsedManifest as multiperiodManifest
} from './manifests/multiperiod.js';
@@ -77,6 +81,10 @@ QUnit.test('has parse', function(assert) {
name: '608-captions',
input: cc608CaptionsTemplate,
expected: cc608CaptionsManifest
+}, {
+ name: '708-captions',
+ input: cc708CaptionsTemplate,
+ expected: cc708CaptionsManifest
}, {
name: 'multiperiod',
input: multiperiod,
diff --git a/test/inheritAttributes.test.js b/test/inheritAttributes.test.js
index 20cff470..70317d2a 100644
--- a/test/inheritAttributes.test.js
+++ b/test/inheritAttributes.test.js
@@ -240,6 +240,199 @@ QUnit.test('parsed 608 metadata', function(assert) {
}], 'eng;CC3');
});
+QUnit.test('parsed 708 metadata', function(assert) {
+ const getmd = (value) => ({
+ schemeIdUri: 'urn:scte:dash:cc:cea-708:2015',
+ value
+ });
+
+ const assertServices = (services, expected, message) => {
+ if (!services) {
+ assert.notOk(expected, message);
+ return;
+ }
+
+ services.forEach((service, i) => {
+ assert.deepEqual(service, expected[i], message);
+ });
+ };
+
+ assertServices(parseCaptionServiceMetadata({
+ schemeIdUri: 'random scheme',
+ value: 'eng'
+ }), undefined, 'dont parse incorrect scheme for 708');
+
+ assertServices(parseCaptionServiceMetadata(getmd('eng')), [{
+ 'channel': undefined,
+ 'language': 'eng',
+ 'aspectRatio': 1,
+ 'easyReader': 0,
+ '3D': 0
+ }], 'simple eng');
+
+ assertServices(parseCaptionServiceMetadata(getmd('eng;swe')), [{
+ 'channel': undefined,
+ 'language': 'eng',
+ 'aspectRatio': 1,
+ 'easyReader': 0,
+ '3D': 0
+ }, {
+ 'channel': undefined,
+ 'language': 'swe',
+ 'aspectRatio': 1,
+ 'easyReader': 0,
+ '3D': 0
+ }], 'eng;swe');
+
+ assertServices(parseCaptionServiceMetadata(getmd('1=lang:eng;2=lang:swe')), [{
+ 'channel': 'SERVICE1',
+ 'language': 'eng',
+ 'aspectRatio': 1,
+ 'easyReader': 0,
+ '3D': 0
+ }, {
+ 'channel': 'SERVICE2',
+ 'language': 'swe',
+ 'aspectRatio': 1,
+ 'easyReader': 0,
+ '3D': 0
+ }], '1=lang:eng;2=lang:swe');
+
+ assertServices(parseCaptionServiceMetadata(getmd('1=lang:eng;swe')), [{
+ 'channel': 'SERVICE1',
+ 'language': 'eng',
+ 'aspectRatio': 1,
+ 'easyReader': 0,
+ '3D': 0
+ }, {
+ 'channel': undefined,
+ 'language': 'swe',
+ 'aspectRatio': 1,
+ 'easyReader': 0,
+ '3D': 0
+ }], 'mixed 1=lang:eng;swe');
+
+ assertServices(parseCaptionServiceMetadata(getmd('1=lang:eng;2=lang:eng,war:1,er:1')), [{
+ 'channel': 'SERVICE1',
+ 'language': 'eng',
+ 'aspectRatio': 1,
+ 'easyReader': 0,
+ '3D': 0
+ }, {
+ 'channel': 'SERVICE2',
+ 'language': 'eng',
+ 'aspectRatio': 1,
+ 'easyReader': 1,
+ '3D': 0
+ }], '1=lang:eng;2=lang:eng,war:1,er:1');
+
+ assertServices(parseCaptionServiceMetadata(getmd('1=lang:eng,war:0;2=lang:eng,3D:1,er:1')), [{
+ 'channel': 'SERVICE1',
+ 'language': 'eng',
+ 'aspectRatio': 0,
+ 'easyReader': 0,
+ '3D': 0
+ }, {
+ 'channel': 'SERVICE2',
+ 'language': 'eng',
+ 'aspectRatio': 1,
+ 'easyReader': 1,
+ '3D': 1
+ }], '1=lang:eng,war:0;2=lang:eng,3D:1,er:1');
+
+ assertServices(parseCaptionServiceMetadata(getmd('eng;fre;spa;jpn;deu;swe;kor;lat;zho;heb;rus;ara;hin;por;tur')), [{
+ 'channel': undefined,
+ 'language': 'eng',
+ 'aspectRatio': 1,
+ 'easyReader': 0,
+ '3D': 0
+ }, {
+ 'channel': undefined,
+ 'language': 'fre',
+ 'aspectRatio': 1,
+ 'easyReader': 0,
+ '3D': 0
+ }, {
+ 'channel': undefined,
+ 'language': 'spa',
+ 'aspectRatio': 1,
+ 'easyReader': 0,
+ '3D': 0
+ }, {
+ 'channel': undefined,
+ 'language': 'jpn',
+ 'aspectRatio': 1,
+ 'easyReader': 0,
+ '3D': 0
+ }, {
+ 'channel': undefined,
+ 'language': 'deu',
+ 'aspectRatio': 1,
+ 'easyReader': 0,
+ '3D': 0
+ }, {
+ 'channel': undefined,
+ 'language': 'swe',
+ 'aspectRatio': 1,
+ 'easyReader': 0,
+ '3D': 0
+ }, {
+ 'channel': undefined,
+ 'language': 'kor',
+ 'aspectRatio': 1,
+ 'easyReader': 0,
+ '3D': 0
+ }, {
+ 'channel': undefined,
+ 'language': 'lat',
+ 'aspectRatio': 1,
+ 'easyReader': 0,
+ '3D': 0
+ }, {
+ 'channel': undefined,
+ 'language': 'zho',
+ 'aspectRatio': 1,
+ 'easyReader': 0,
+ '3D': 0
+ }, {
+ 'channel': undefined,
+ 'language': 'heb',
+ 'aspectRatio': 1,
+ 'easyReader': 0,
+ '3D': 0
+ }, {
+ 'channel': undefined,
+ 'language': 'rus',
+ 'aspectRatio': 1,
+ 'easyReader': 0,
+ '3D': 0
+ }, {
+ 'channel': undefined,
+ 'language': 'ara',
+ 'aspectRatio': 1,
+ 'easyReader': 0,
+ '3D': 0
+ }, {
+ 'channel': undefined,
+ 'language': 'hin',
+ 'aspectRatio': 1,
+ 'easyReader': 0,
+ '3D': 0
+ }, {
+ 'channel': undefined,
+ 'language': 'por',
+ 'aspectRatio': 1,
+ 'easyReader': 0,
+ '3D': 0
+ }, {
+ 'channel': undefined,
+ 'language': 'tur',
+ 'aspectRatio': 1,
+ 'easyReader': 0,
+ '3D': 0
+ }], 'make sure that parsing 15 services works');
+});
+
QUnit.module('inheritAttributes');
QUnit.test('needs at least one Period', function(assert) {
diff --git a/test/manifests/608-captions.mpd b/test/manifests/608-captions.mpd
index 1f050698..499f72e4 100644
--- a/test/manifests/608-captions.mpd
+++ b/test/manifests/608-captions.mpd
@@ -7,7 +7,7 @@
-
+
1080p.ts
diff --git a/test/manifests/708-captions.js b/test/manifests/708-captions.js
new file mode 100644
index 00000000..eae5dfd0
--- /dev/null
+++ b/test/manifests/708-captions.js
@@ -0,0 +1,71 @@
+export const parsedManifest = {
+ allowCache: true,
+ discontinuityStarts: [],
+ duration: 6,
+ endList: true,
+ mediaGroups: {
+ 'AUDIO': {},
+ 'CLOSED-CAPTIONS': {
+ cc: {
+ // eng: {
+ // autoselect: false,
+ // default: false,
+ // instreamId: '1',
+ // language: 'eng',
+ // aspectRatio: 1,
+ // easyReader: 0,
+ // '3D': 0
+ // },
+ // TODO only this one ends up being represented and not both
+ eng: {
+ 'autoselect': false,
+ 'default': false,
+ 'instreamId': 'SERVICE2',
+ 'language': 'eng',
+ 'aspectRatio': 1,
+ 'easyReader': 1,
+ '3D': 0
+ }
+ }
+ },
+ 'SUBTITLES': {},
+ 'VIDEO': {}
+ },
+ playlists: [
+ {
+ attributes: {
+ 'AUDIO': 'audio',
+ 'BANDWIDTH': 449000,
+ 'CODECS': 'avc1.420015',
+ 'NAME': '482',
+ 'PROGRAM-ID': 1,
+ 'RESOLUTION': {
+ height: 270,
+ width: 482
+ },
+ 'SUBTITLES': 'subs'
+ },
+ endList: true,
+ resolvedUri: '',
+ targetDuration: 6,
+ mediaSequence: 0,
+ segments: [
+ {
+ duration: 6,
+ timeline: 0,
+ number: 0,
+ map: {
+ uri: '',
+ resolvedUri: 'https://www.example.com/1080p.ts'
+ },
+ resolvedUri: 'https://www.example.com/1080p.ts',
+ uri: 'https://www.example.com/1080p.ts'
+ }
+ ],
+ timeline: 0,
+ uri: ''
+ }
+ ],
+ segments: [],
+ uri: ''
+};
diff --git a/test/manifests/708-captions.mpd b/test/manifests/708-captions.mpd
new file mode 100644
index 00000000..fc71f2bb
--- /dev/null
+++ b/test/manifests/708-captions.mpd
@@ -0,0 +1,17 @@
+
+
+ https://www.example.com/base
+
+
+
+
+
+
+
+ 1080p.ts
+
+
+
+
+
+