inheritAttributes.test.js 86 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959
  1. import {
  2. inheritAttributes,
  3. buildBaseUrls,
  4. parseCaptionServiceMetadata,
  5. getSegmentInformation,
  6. getPeriodStart,
  7. toEventStream
  8. } from '../src/inheritAttributes';
  9. import { stringToMpdXml } from '../src/stringToMpdXml';
  10. import errors from '../src/errors';
  11. import QUnit from 'qunit';
  12. import { stub } from 'sinon';
  13. import { toPlaylists } from '../src/toPlaylists';
  14. import decodeB64ToUint8Array from '@videojs/vhs-utils/es/decode-b64-to-uint8-array';
  15. import { findChildren } from '../src/utils/xml';
  16. QUnit.module('buildBaseUrls');
  17. QUnit.test('returns reference urls when no BaseURL nodes', function(assert) {
  18. const reference = [{ baseUrl: 'https://example.com/' }, { baseUrl: 'https://foo.com/' }];
  19. assert.deepEqual(buildBaseUrls(reference, []), reference, 'returns reference urls');
  20. });
  21. QUnit.test('single reference url with single BaseURL node', function(assert) {
  22. const reference = [{ baseUrl: 'https://example.com' }];
  23. const node = [{ textContent: 'bar/' }];
  24. const expected = [{ baseUrl: 'https://example.com/bar/' }];
  25. assert.deepEqual(buildBaseUrls(reference, node), expected, 'builds base url');
  26. });
  27. QUnit.test('multiple reference urls with single BaseURL node', function(assert) {
  28. const reference = [{ baseUrl: 'https://example.com/' }, { baseUrl: 'https://foo.com/' }];
  29. const node = [{ textContent: 'bar/' }];
  30. const expected = [{ baseUrl: 'https://example.com/bar/' }, { baseUrl: 'https://foo.com/bar/' }];
  31. assert.deepEqual(
  32. buildBaseUrls(reference, node), expected,
  33. 'base url for each reference url'
  34. );
  35. });
  36. QUnit.test('multiple BaseURL nodes with single reference url', function(assert) {
  37. const reference = [{ baseUrl: 'https://example.com/' }];
  38. const nodes = [{ textContent: 'bar/' }, { textContent: 'baz/' }];
  39. const expected = [{ baseUrl: 'https://example.com/bar/' }, { baseUrl: 'https://example.com/baz/' }];
  40. assert.deepEqual(buildBaseUrls(reference, nodes), expected, 'base url for each node');
  41. });
  42. QUnit.test('multiple reference urls with multiple BaseURL nodes', function(assert) {
  43. const reference = [
  44. { baseUrl: 'https://example.com/' }, { baseUrl: 'https://foo.com/' }, { baseUrl: 'http://example.com' }
  45. ];
  46. const nodes =
  47. [{ textContent: 'bar/' }, { textContent: 'baz/' }, { textContent: 'buzz/' }];
  48. const expected = [
  49. { baseUrl: 'https://example.com/bar/' },
  50. { baseUrl: 'https://example.com/baz/' },
  51. { baseUrl: 'https://example.com/buzz/' },
  52. { baseUrl: 'https://foo.com/bar/' },
  53. { baseUrl: 'https://foo.com/baz/' },
  54. { baseUrl: 'https://foo.com/buzz/' },
  55. { baseUrl: 'http://example.com/bar/' },
  56. { baseUrl: 'http://example.com/baz/' },
  57. { baseUrl: 'http://example.com/buzz/' }
  58. ];
  59. assert.deepEqual(buildBaseUrls(reference, nodes), expected, 'creates all base urls');
  60. });
  61. QUnit.test('absolute BaseURL overwrites reference', function(assert) {
  62. const reference = [{ baseUrl: 'https://example.com' }];
  63. const node = [{ textContent: 'https://foo.com/bar/' }];
  64. const expected = [{ baseUrl: 'https://foo.com/bar/'}];
  65. assert.deepEqual(
  66. buildBaseUrls(reference, node), expected,
  67. 'absolute url overwrites reference'
  68. );
  69. });
  70. QUnit.test('reference attributes are ignored when there is a BaseURL node', function(assert) {
  71. const reference = [{ baseUrl: 'https://example.com', attributes: [{ name: 'test', value: 'wow' }] }];
  72. const node = [{ textContent: 'https://foo.com/bar/' }];
  73. const expected = [{ baseUrl: 'https://foo.com/bar/' }];
  74. assert.deepEqual(
  75. buildBaseUrls(reference, node), expected,
  76. 'baseURL attributes are not included'
  77. );
  78. });
  79. QUnit.test('BasURL attributes are still added with a reference', function(assert) {
  80. const reference = [{ baseUrl: 'https://example.com' }];
  81. const node = [{ textContent: 'https://foo.com/bar/', attributes: [{ name: 'test', value: 'wow' }] }];
  82. const expected = [{ baseUrl: 'https://foo.com/bar/', test: 'wow' }];
  83. assert.deepEqual(
  84. buildBaseUrls(reference, node), expected,
  85. 'baseURL attributes are included'
  86. );
  87. });
  88. QUnit.test('attributes are replaced when both reference and BaseURL have the same attributes', function(assert) {
  89. const reference = [{ baseUrl: 'https://example.com', attributes: [{ name: 'test', value: 'old' }] }];
  90. const node = [{ textContent: 'https://foo.com/bar/', attributes: [{ name: 'test', value: 'new' }] }];
  91. const expected = [{ baseUrl: 'https://foo.com/bar/', test: 'new' }];
  92. assert.deepEqual(
  93. buildBaseUrls(reference, node), expected,
  94. 'baseURL attributes are included'
  95. );
  96. });
  97. QUnit.module('getPeriodStart');
  98. QUnit.test('gets period start when available', function(assert) {
  99. assert.equal(
  100. getPeriodStart({
  101. attributes: { start: 11 },
  102. priorPeriodAttributes: null,
  103. mpdType: 'static'
  104. }),
  105. 11,
  106. 'returned period start value'
  107. );
  108. });
  109. QUnit.test('gets period start when prior period and duration', function(assert) {
  110. assert.equal(
  111. getPeriodStart({
  112. attributes: {},
  113. priorPeriodAttributes: { start: 11, duration: 20 },
  114. mpdType: 'static'
  115. }),
  116. 31,
  117. 'returned correct period start value'
  118. );
  119. });
  120. QUnit.test('gets period start when no prior period and static', function(assert) {
  121. assert.equal(
  122. getPeriodStart({
  123. attributes: {},
  124. priorPeriodAttributes: null,
  125. mpdType: 'static'
  126. }),
  127. 0,
  128. 'returned correct period start value'
  129. );
  130. });
  131. QUnit.test('null when static and prior period but missing attributes', function(assert) {
  132. assert.equal(
  133. getPeriodStart({
  134. attributes: {},
  135. priorPeriodAttributes: { start: 11 },
  136. mpdType: 'static'
  137. }),
  138. null,
  139. 'null when no duration in prior period'
  140. );
  141. assert.equal(
  142. getPeriodStart({
  143. attributes: {},
  144. priorPeriodAttributes: { duration: 20 },
  145. mpdType: 'static'
  146. }),
  147. null,
  148. 'null when no start in prior period'
  149. );
  150. });
  151. QUnit.test('null when dynamic, no prior period, and no start attribute', function(assert) {
  152. assert.equal(
  153. getPeriodStart({
  154. attributes: {},
  155. priorPeriodAttributes: null,
  156. mpdType: 'dyanmic'
  157. }),
  158. null,
  159. 'null when no dynamic, no start attribute, and no prior period'
  160. );
  161. });
  162. QUnit.module('getSegmentInformation');
  163. QUnit.test('undefined Segment information when no Segment nodes', function(assert) {
  164. const adaptationSet = { childNodes: [] };
  165. const expected = {};
  166. assert.deepEqual(
  167. getSegmentInformation(adaptationSet), expected,
  168. 'undefined segment info'
  169. );
  170. });
  171. QUnit.test('gets SegmentTemplate attributes', function(assert) {
  172. const adaptationSet = {
  173. childNodes: [{
  174. tagName: 'SegmentTemplate',
  175. attributes: [{ name: 'media', value: 'video.mp4' }],
  176. childNodes: []
  177. }]
  178. };
  179. const expected = {
  180. template: { media: 'video.mp4' }
  181. };
  182. assert.deepEqual(
  183. getSegmentInformation(adaptationSet), expected,
  184. 'SegmentTemplate info'
  185. );
  186. });
  187. QUnit.test('gets SegmentList attributes', function(assert) {
  188. const adaptationSet = {
  189. childNodes: [{
  190. tagName: 'SegmentList',
  191. attributes: [{ name: 'duration', value: '10' }],
  192. childNodes: []
  193. }]
  194. };
  195. const expected = {
  196. list: {
  197. duration: 10,
  198. segmentUrls: [],
  199. initialization: {}
  200. }
  201. };
  202. assert.deepEqual(
  203. getSegmentInformation(adaptationSet), expected,
  204. 'SegmentList info'
  205. );
  206. });
  207. QUnit.test('gets SegmentBase attributes', function(assert) {
  208. const adaptationSet = {
  209. childNodes: [{
  210. tagName: 'SegmentBase',
  211. attributes: [{ name: 'duration', value: '10' }],
  212. childNodes: []
  213. }]
  214. };
  215. const expected = {
  216. base: { duration: 10, initialization: {} }
  217. };
  218. assert.deepEqual(
  219. getSegmentInformation(adaptationSet), expected,
  220. 'SegmentBase info'
  221. );
  222. });
  223. QUnit.test('gets SegmentTemplate and SegmentTimeline attributes', function(assert) {
  224. const adaptationSet = {
  225. childNodes: [{
  226. tagName: 'SegmentTemplate',
  227. attributes: [{ name: 'media', value: 'video.mp4' }],
  228. childNodes: [{
  229. tagName: 'SegmentTimeline',
  230. childNodes: [{
  231. tagName: 'S',
  232. attributes: [{ name: 'd', value: '10' }]
  233. }, {
  234. tagName: 'S',
  235. attributes: [{ name: 'd', value: '5' }]
  236. }, {
  237. tagName: 'S',
  238. attributes: [{ name: 'd', value: '7' }]
  239. }]
  240. }]
  241. }]
  242. };
  243. const expected = {
  244. template: { media: 'video.mp4' },
  245. segmentTimeline: [{ d: 10 }, { d: 5 }, { d: 7 }]
  246. };
  247. assert.deepEqual(
  248. getSegmentInformation(adaptationSet), expected,
  249. 'SegmentTemplate and SegmentTimeline info'
  250. );
  251. });
  252. QUnit.module('caption service metadata');
  253. QUnit.test('parsed 608 metadata', function(assert) {
  254. const getmd = (value) => ({
  255. schemeIdUri: 'urn:scte:dash:cc:cea-608:2015',
  256. value
  257. });
  258. const assertServices = (services, expected, message) => {
  259. if (!services) {
  260. assert.notOk(expected, message);
  261. return;
  262. }
  263. services.forEach((service, i) => {
  264. assert.deepEqual(service, expected[i], message);
  265. });
  266. };
  267. assertServices(parseCaptionServiceMetadata({
  268. schemeIdUri: 'random scheme',
  269. value: 'CC1'
  270. }), undefined, 'dont parse incorrect scheme ID for 608');
  271. assertServices(parseCaptionServiceMetadata(getmd('CC1')), [{
  272. channel: 'CC1',
  273. language: 'CC1'
  274. }], 'CC1');
  275. assertServices(parseCaptionServiceMetadata(getmd('CC2')), [{
  276. channel: 'CC2',
  277. language: 'CC2'
  278. }], 'CC2');
  279. assertServices(parseCaptionServiceMetadata(getmd('English')), [{
  280. channel: undefined,
  281. language: 'English'
  282. }], 'English');
  283. assertServices(parseCaptionServiceMetadata(getmd('CC1;CC2')), [{
  284. channel: 'CC1',
  285. language: 'CC1'
  286. }, {
  287. channel: 'CC2',
  288. language: 'CC2'
  289. }], 'CC1;CC2');
  290. assertServices(parseCaptionServiceMetadata(getmd('CC1=eng;CC3=swe')), [{
  291. channel: 'CC1',
  292. language: 'eng'
  293. }, {
  294. channel: 'CC3',
  295. language: 'swe'
  296. }], 'CC1=eng;CC3=swe');
  297. assertServices(parseCaptionServiceMetadata(getmd('CC1=Hello;CC3=World')), [{
  298. channel: 'CC1',
  299. language: 'Hello'
  300. }, {
  301. channel: 'CC3',
  302. language: 'World'
  303. }], 'CC1=Hello;CC3=World');
  304. assertServices(parseCaptionServiceMetadata(getmd('eng;swe')), [{
  305. channel: undefined,
  306. language: 'eng'
  307. }, {
  308. channel: undefined,
  309. language: 'swe'
  310. }], 'eng;CC3');
  311. });
  312. QUnit.test('parsed 708 metadata', function(assert) {
  313. const getmd = (value) => ({
  314. schemeIdUri: 'urn:scte:dash:cc:cea-708:2015',
  315. value
  316. });
  317. const assertServices = (services, expected, message) => {
  318. if (!services) {
  319. assert.notOk(expected, message);
  320. return;
  321. }
  322. services.forEach((service, i) => {
  323. assert.deepEqual(service, expected[i], message);
  324. });
  325. };
  326. assertServices(parseCaptionServiceMetadata({
  327. schemeIdUri: 'random scheme',
  328. value: 'eng'
  329. }), undefined, 'dont parse incorrect scheme for 708');
  330. assertServices(parseCaptionServiceMetadata(getmd('eng')), [{
  331. 'channel': undefined,
  332. 'language': 'eng',
  333. 'aspectRatio': 1,
  334. 'easyReader': 0,
  335. '3D': 0
  336. }], 'simple eng');
  337. assertServices(parseCaptionServiceMetadata(getmd('eng;swe')), [{
  338. 'channel': undefined,
  339. 'language': 'eng',
  340. 'aspectRatio': 1,
  341. 'easyReader': 0,
  342. '3D': 0
  343. }, {
  344. 'channel': undefined,
  345. 'language': 'swe',
  346. 'aspectRatio': 1,
  347. 'easyReader': 0,
  348. '3D': 0
  349. }], 'eng;swe');
  350. assertServices(parseCaptionServiceMetadata(getmd('1=lang:eng;2=lang:swe')), [{
  351. 'channel': 'SERVICE1',
  352. 'language': 'eng',
  353. 'aspectRatio': 1,
  354. 'easyReader': 0,
  355. '3D': 0
  356. }, {
  357. 'channel': 'SERVICE2',
  358. 'language': 'swe',
  359. 'aspectRatio': 1,
  360. 'easyReader': 0,
  361. '3D': 0
  362. }], '1=lang:eng;2=lang:swe');
  363. assertServices(parseCaptionServiceMetadata(getmd('1=lang:eng;swe')), [{
  364. 'channel': 'SERVICE1',
  365. 'language': 'eng',
  366. 'aspectRatio': 1,
  367. 'easyReader': 0,
  368. '3D': 0
  369. }, {
  370. 'channel': undefined,
  371. 'language': 'swe',
  372. 'aspectRatio': 1,
  373. 'easyReader': 0,
  374. '3D': 0
  375. }], 'mixed 1=lang:eng;swe');
  376. assertServices(parseCaptionServiceMetadata(getmd('1=lang:eng;2=lang:eng,war:1,er:1')), [{
  377. 'channel': 'SERVICE1',
  378. 'language': 'eng',
  379. 'aspectRatio': 1,
  380. 'easyReader': 0,
  381. '3D': 0
  382. }, {
  383. 'channel': 'SERVICE2',
  384. 'language': 'eng',
  385. 'aspectRatio': 1,
  386. 'easyReader': 1,
  387. '3D': 0
  388. }], '1=lang:eng;2=lang:eng,war:1,er:1');
  389. assertServices(parseCaptionServiceMetadata(getmd('1=lang:eng,war:0;2=lang:eng,3D:1,er:1')), [{
  390. 'channel': 'SERVICE1',
  391. 'language': 'eng',
  392. 'aspectRatio': 0,
  393. 'easyReader': 0,
  394. '3D': 0
  395. }, {
  396. 'channel': 'SERVICE2',
  397. 'language': 'eng',
  398. 'aspectRatio': 1,
  399. 'easyReader': 1,
  400. '3D': 1
  401. }], '1=lang:eng,war:0;2=lang:eng,3D:1,er:1');
  402. assertServices(parseCaptionServiceMetadata(getmd('eng;fre;spa;jpn;deu;swe;kor;lat;zho;heb;rus;ara;hin;por;tur')), [{
  403. 'channel': undefined,
  404. 'language': 'eng',
  405. 'aspectRatio': 1,
  406. 'easyReader': 0,
  407. '3D': 0
  408. }, {
  409. 'channel': undefined,
  410. 'language': 'fre',
  411. 'aspectRatio': 1,
  412. 'easyReader': 0,
  413. '3D': 0
  414. }, {
  415. 'channel': undefined,
  416. 'language': 'spa',
  417. 'aspectRatio': 1,
  418. 'easyReader': 0,
  419. '3D': 0
  420. }, {
  421. 'channel': undefined,
  422. 'language': 'jpn',
  423. 'aspectRatio': 1,
  424. 'easyReader': 0,
  425. '3D': 0
  426. }, {
  427. 'channel': undefined,
  428. 'language': 'deu',
  429. 'aspectRatio': 1,
  430. 'easyReader': 0,
  431. '3D': 0
  432. }, {
  433. 'channel': undefined,
  434. 'language': 'swe',
  435. 'aspectRatio': 1,
  436. 'easyReader': 0,
  437. '3D': 0
  438. }, {
  439. 'channel': undefined,
  440. 'language': 'kor',
  441. 'aspectRatio': 1,
  442. 'easyReader': 0,
  443. '3D': 0
  444. }, {
  445. 'channel': undefined,
  446. 'language': 'lat',
  447. 'aspectRatio': 1,
  448. 'easyReader': 0,
  449. '3D': 0
  450. }, {
  451. 'channel': undefined,
  452. 'language': 'zho',
  453. 'aspectRatio': 1,
  454. 'easyReader': 0,
  455. '3D': 0
  456. }, {
  457. 'channel': undefined,
  458. 'language': 'heb',
  459. 'aspectRatio': 1,
  460. 'easyReader': 0,
  461. '3D': 0
  462. }, {
  463. 'channel': undefined,
  464. 'language': 'rus',
  465. 'aspectRatio': 1,
  466. 'easyReader': 0,
  467. '3D': 0
  468. }, {
  469. 'channel': undefined,
  470. 'language': 'ara',
  471. 'aspectRatio': 1,
  472. 'easyReader': 0,
  473. '3D': 0
  474. }, {
  475. 'channel': undefined,
  476. 'language': 'hin',
  477. 'aspectRatio': 1,
  478. 'easyReader': 0,
  479. '3D': 0
  480. }, {
  481. 'channel': undefined,
  482. 'language': 'por',
  483. 'aspectRatio': 1,
  484. 'easyReader': 0,
  485. '3D': 0
  486. }, {
  487. 'channel': undefined,
  488. 'language': 'tur',
  489. 'aspectRatio': 1,
  490. 'easyReader': 0,
  491. '3D': 0
  492. }], 'make sure that parsing 15 services works');
  493. });
  494. QUnit.module('inheritAttributes');
  495. QUnit.test('needs at least one Period', function(assert) {
  496. assert.throws(
  497. () => inheritAttributes(stringToMpdXml('<MPD></MPD>')),
  498. new RegExp(errors.INVALID_NUMBER_OF_PERIOD)
  499. );
  500. });
  501. QUnit.test('end to end - basic', function(assert) {
  502. const NOW = Date.now();
  503. const actual = inheritAttributes(stringToMpdXml(`
  504. <MPD mediaPresentationDuration="PT30S" >
  505. <BaseURL>https://www.example.com/base/</BaseURL>
  506. <Period>
  507. <AdaptationSet mimeType="video/mp4" >
  508. <Role value="main"></Role>
  509. <SegmentTemplate></SegmentTemplate>
  510. <Representation
  511. bandwidth="5000000"
  512. codecs="avc1.64001e"
  513. height="404"
  514. id="test"
  515. width="720">
  516. </Representation>
  517. </AdaptationSet>
  518. <AdaptationSet mimeType="text/vtt" lang="en">
  519. <Representation bandwidth="256" id="en">
  520. <BaseURL>https://example.com/en.vtt</BaseURL>
  521. </Representation>
  522. </AdaptationSet>
  523. </Period>
  524. </MPD>
  525. `), { NOW });
  526. const expected = {
  527. contentSteeringInfo: null,
  528. eventStream: [],
  529. locations: undefined,
  530. representationInfo: [{
  531. attributes: {
  532. bandwidth: 5000000,
  533. baseUrl: 'https://www.example.com/base/',
  534. codecs: 'avc1.64001e',
  535. height: 404,
  536. id: 'test',
  537. mediaPresentationDuration: 30,
  538. mimeType: 'video/mp4',
  539. periodStart: 0,
  540. role: {
  541. value: 'main'
  542. },
  543. sourceDuration: 30,
  544. type: 'static',
  545. width: 720,
  546. NOW,
  547. clientOffset: 0
  548. },
  549. segmentInfo: {
  550. template: {}
  551. }
  552. }, {
  553. attributes: {
  554. bandwidth: 256,
  555. baseUrl: 'https://example.com/en.vtt',
  556. id: 'en',
  557. lang: 'en',
  558. mediaPresentationDuration: 30,
  559. mimeType: 'text/vtt',
  560. periodStart: 0,
  561. role: {},
  562. sourceDuration: 30,
  563. type: 'static',
  564. NOW,
  565. clientOffset: 0
  566. },
  567. segmentInfo: {}
  568. }]
  569. };
  570. assert.equal(actual.representationInfo.length, 2);
  571. assert.deepEqual(actual, expected);
  572. });
  573. QUnit.test('end to end - basic using manifest uri', function(assert) {
  574. const NOW = Date.now();
  575. const actual = inheritAttributes(stringToMpdXml(`
  576. <MPD mediaPresentationDuration="PT30S" >
  577. <BaseURL>base/</BaseURL>
  578. <Period>
  579. <AdaptationSet mimeType="video/mp4" >
  580. <Role value="main"></Role>
  581. <SegmentTemplate></SegmentTemplate>
  582. <Representation
  583. bandwidth="5000000"
  584. codecs="avc1.64001e"
  585. height="404"
  586. id="test"
  587. width="720">
  588. </Representation>
  589. </AdaptationSet>
  590. <AdaptationSet mimeType="text/vtt" lang="en">
  591. <Representation bandwidth="256" id="en">
  592. <BaseURL>en.vtt</BaseURL>
  593. </Representation>
  594. </AdaptationSet>
  595. </Period>
  596. </MPD>
  597. `), { NOW, manifestUri: 'https://www.test.com' });
  598. const expected = {
  599. contentSteeringInfo: null,
  600. eventStream: [],
  601. locations: undefined,
  602. representationInfo: [{
  603. attributes: {
  604. bandwidth: 5000000,
  605. baseUrl: 'https://www.test.com/base/',
  606. codecs: 'avc1.64001e',
  607. height: 404,
  608. id: 'test',
  609. mediaPresentationDuration: 30,
  610. mimeType: 'video/mp4',
  611. periodStart: 0,
  612. role: {
  613. value: 'main'
  614. },
  615. sourceDuration: 30,
  616. type: 'static',
  617. width: 720,
  618. NOW,
  619. clientOffset: 0
  620. },
  621. segmentInfo: {
  622. template: {}
  623. }
  624. }, {
  625. attributes: {
  626. bandwidth: 256,
  627. baseUrl: 'https://www.test.com/base/en.vtt',
  628. id: 'en',
  629. lang: 'en',
  630. mediaPresentationDuration: 30,
  631. mimeType: 'text/vtt',
  632. periodStart: 0,
  633. role: {},
  634. sourceDuration: 30,
  635. type: 'static',
  636. NOW,
  637. clientOffset: 0
  638. },
  639. segmentInfo: {}
  640. }]
  641. };
  642. assert.equal(actual.representationInfo.length, 2);
  643. assert.deepEqual(actual, expected);
  644. });
  645. QUnit.test('end to end - basic dynamic', function(assert) {
  646. const NOW = Date.now();
  647. const actual = inheritAttributes(stringToMpdXml(`
  648. <MPD type="dyanmic">
  649. <BaseURL>https://www.example.com/base/</BaseURL>
  650. <Period start="PT0S">
  651. <AdaptationSet mimeType="video/mp4">
  652. <Role value="main"></Role>
  653. <SegmentTemplate></SegmentTemplate>
  654. <Representation
  655. bandwidth="5000000"
  656. codecs="avc1.64001e"
  657. height="404"
  658. id="test"
  659. width="720">
  660. </Representation>
  661. </AdaptationSet>
  662. <AdaptationSet mimeType="text/vtt" lang="en">
  663. <Representation bandwidth="256" id="en">
  664. <BaseURL>https://example.com/en.vtt</BaseURL>
  665. </Representation>
  666. </AdaptationSet>
  667. </Period>
  668. </MPD>
  669. `), { NOW });
  670. const expected = {
  671. contentSteeringInfo: null,
  672. eventStream: [],
  673. locations: undefined,
  674. representationInfo: [{
  675. attributes: {
  676. bandwidth: 5000000,
  677. baseUrl: 'https://www.example.com/base/',
  678. codecs: 'avc1.64001e',
  679. height: 404,
  680. id: 'test',
  681. mimeType: 'video/mp4',
  682. periodStart: 0,
  683. role: {
  684. value: 'main'
  685. },
  686. sourceDuration: 0,
  687. type: 'dyanmic',
  688. width: 720,
  689. NOW,
  690. clientOffset: 0
  691. },
  692. segmentInfo: {
  693. template: {}
  694. }
  695. }, {
  696. attributes: {
  697. bandwidth: 256,
  698. baseUrl: 'https://example.com/en.vtt',
  699. id: 'en',
  700. lang: 'en',
  701. mimeType: 'text/vtt',
  702. periodStart: 0,
  703. role: {},
  704. sourceDuration: 0,
  705. type: 'dyanmic',
  706. NOW,
  707. clientOffset: 0
  708. },
  709. segmentInfo: {}
  710. }]
  711. };
  712. assert.equal(actual.representationInfo.length, 2);
  713. assert.deepEqual(actual, expected);
  714. });
  715. QUnit.test('end to end - content steering - non resolvable base URLs', function(assert) {
  716. const NOW = Date.now();
  717. const actual = inheritAttributes(stringToMpdXml(`
  718. <MPD type="dyanmic">
  719. <ContentSteering defaultServiceLocation="beta" queryBeforeStart="false" proxyServerURL="http://127.0.0.1:3455/steer">https://example.com/app/url</ContentSteering>
  720. <BaseURL serviceLocation="alpha">https://cdn1.example.com/</BaseURL>
  721. <BaseURL serviceLocation="beta">https://cdn2.example.com/</BaseURL>
  722. <Period start="PT0S">
  723. <AdaptationSet mimeType="video/mp4">
  724. <Role value="main"></Role>
  725. <SegmentTemplate></SegmentTemplate>
  726. <Representation
  727. bandwidth="5000000"
  728. codecs="avc1.64001e"
  729. height="404"
  730. id="test"
  731. width="720">
  732. </Representation>
  733. </AdaptationSet>
  734. <AdaptationSet mimeType="text/vtt" lang="en">
  735. <Representation bandwidth="256" id="en">
  736. <BaseURL>https://example.com/en.vtt</BaseURL>
  737. </Representation>
  738. </AdaptationSet>
  739. </Period>
  740. </MPD>
  741. `), { NOW, manifestUri: 'https://www.test.com' });
  742. // Note that we expect to see the `contentSteeringInfo` object set with the
  743. // proper values. We also expect to see the `serviceLocation` property set to
  744. // the correct values inside of the correct representations.
  745. const expected = {
  746. contentSteeringInfo: {
  747. defaultServiceLocation: 'beta',
  748. proxyServerURL: 'http://127.0.0.1:3455/steer',
  749. queryBeforeStart: false,
  750. serverURL: 'https://example.com/app/url'
  751. },
  752. eventStream: [],
  753. locations: undefined,
  754. representationInfo: [
  755. {
  756. attributes: {
  757. NOW,
  758. bandwidth: 5000000,
  759. baseUrl: 'https://cdn1.example.com/',
  760. clientOffset: 0,
  761. codecs: 'avc1.64001e',
  762. height: 404,
  763. id: 'test',
  764. mimeType: 'video/mp4',
  765. periodStart: 0,
  766. role: {
  767. value: 'main'
  768. },
  769. serviceLocation: 'alpha',
  770. sourceDuration: 0,
  771. type: 'dyanmic',
  772. width: 720
  773. },
  774. segmentInfo: {
  775. template: {}
  776. }
  777. },
  778. {
  779. attributes: {
  780. NOW,
  781. bandwidth: 5000000,
  782. baseUrl: 'https://cdn2.example.com/',
  783. clientOffset: 0,
  784. codecs: 'avc1.64001e',
  785. height: 404,
  786. id: 'test',
  787. mimeType: 'video/mp4',
  788. periodStart: 0,
  789. role: {
  790. value: 'main'
  791. },
  792. serviceLocation: 'beta',
  793. sourceDuration: 0,
  794. type: 'dyanmic',
  795. width: 720
  796. },
  797. segmentInfo: {
  798. template: {}
  799. }
  800. },
  801. {
  802. attributes: {
  803. NOW,
  804. bandwidth: 256,
  805. baseUrl: 'https://example.com/en.vtt',
  806. clientOffset: 0,
  807. id: 'en',
  808. lang: 'en',
  809. mimeType: 'text/vtt',
  810. periodStart: 0,
  811. role: {},
  812. sourceDuration: 0,
  813. type: 'dyanmic'
  814. },
  815. segmentInfo: {}
  816. },
  817. {
  818. attributes: {
  819. NOW,
  820. bandwidth: 256,
  821. baseUrl: 'https://example.com/en.vtt',
  822. clientOffset: 0,
  823. id: 'en',
  824. lang: 'en',
  825. mimeType: 'text/vtt',
  826. periodStart: 0,
  827. role: {},
  828. sourceDuration: 0,
  829. type: 'dyanmic'
  830. },
  831. segmentInfo: {}
  832. }
  833. ]
  834. };
  835. assert.equal(actual.representationInfo.length, 4);
  836. assert.deepEqual(actual, expected);
  837. });
  838. QUnit.test('end to end - content steering - resolvable base URLs', function(assert) {
  839. const NOW = Date.now();
  840. const actual = inheritAttributes(stringToMpdXml(`
  841. <MPD type="dyanmic">
  842. <ContentSteering defaultServiceLocation="beta" queryBeforeStart="false" proxyServerURL="http://127.0.0.1:3455/steer">https://example.com/app/url</ContentSteering>
  843. <BaseURL serviceLocation="alpha">https://cdn1.example.com/</BaseURL>
  844. <BaseURL serviceLocation="beta">https://cdn2.example.com/</BaseURL>
  845. <Period start="PT0S">
  846. <AdaptationSet mimeType="video/mp4">
  847. <Role value="main"></Role>
  848. <SegmentTemplate></SegmentTemplate>
  849. <Representation
  850. bandwidth="5000000"
  851. codecs="avc1.64001e"
  852. height="404"
  853. id="test"
  854. width="720">
  855. </Representation>
  856. <BaseURL>/video</BaseURL>
  857. </AdaptationSet>
  858. <AdaptationSet mimeType="text/vtt" lang="en">
  859. <Representation bandwidth="256" id="en">
  860. <BaseURL>/vtt</BaseURL>
  861. </Representation>
  862. </AdaptationSet>
  863. </Period>
  864. </MPD>
  865. `), { NOW, manifestUri: 'https://www.test.com' });
  866. // Note that we expect to see the `contentSteeringInfo` object set with the
  867. // proper values. We also expect to see the `serviceLocation` property set to
  868. // the correct values inside of the correct representations.
  869. //
  870. // Also note that some of the representations have '/video' appended
  871. // to the end of the baseUrls
  872. const expected = {
  873. contentSteeringInfo: {
  874. defaultServiceLocation: 'beta',
  875. proxyServerURL: 'http://127.0.0.1:3455/steer',
  876. queryBeforeStart: false,
  877. serverURL: 'https://example.com/app/url'
  878. },
  879. eventStream: [],
  880. locations: undefined,
  881. representationInfo: [
  882. {
  883. attributes: {
  884. NOW,
  885. bandwidth: 5000000,
  886. baseUrl: 'https://cdn1.example.com/video',
  887. clientOffset: 0,
  888. codecs: 'avc1.64001e',
  889. height: 404,
  890. id: 'test',
  891. mimeType: 'video/mp4',
  892. periodStart: 0,
  893. role: {
  894. value: 'main'
  895. },
  896. serviceLocation: 'alpha',
  897. sourceDuration: 0,
  898. type: 'dyanmic',
  899. width: 720
  900. },
  901. segmentInfo: {
  902. template: {}
  903. }
  904. },
  905. {
  906. attributes: {
  907. NOW,
  908. bandwidth: 5000000,
  909. baseUrl: 'https://cdn2.example.com/video',
  910. clientOffset: 0,
  911. codecs: 'avc1.64001e',
  912. height: 404,
  913. id: 'test',
  914. mimeType: 'video/mp4',
  915. periodStart: 0,
  916. role: {
  917. value: 'main'
  918. },
  919. serviceLocation: 'beta',
  920. sourceDuration: 0,
  921. type: 'dyanmic',
  922. width: 720
  923. },
  924. segmentInfo: {
  925. template: {}
  926. }
  927. },
  928. {
  929. attributes: {
  930. NOW,
  931. bandwidth: 256,
  932. baseUrl: 'https://cdn1.example.com/vtt',
  933. clientOffset: 0,
  934. id: 'en',
  935. lang: 'en',
  936. mimeType: 'text/vtt',
  937. periodStart: 0,
  938. role: {},
  939. serviceLocation: 'alpha',
  940. sourceDuration: 0,
  941. type: 'dyanmic'
  942. },
  943. segmentInfo: {}
  944. },
  945. {
  946. attributes: {
  947. NOW,
  948. bandwidth: 256,
  949. baseUrl: 'https://cdn2.example.com/vtt',
  950. clientOffset: 0,
  951. id: 'en',
  952. lang: 'en',
  953. mimeType: 'text/vtt',
  954. periodStart: 0,
  955. role: {},
  956. serviceLocation: 'beta',
  957. sourceDuration: 0,
  958. type: 'dyanmic'
  959. },
  960. segmentInfo: {}
  961. }
  962. ]
  963. };
  964. assert.equal(actual.representationInfo.length, 4);
  965. assert.deepEqual(actual, expected);
  966. });
  967. QUnit.test('Too many content steering tags sends a warning to the eventHandler', function(assert) {
  968. const handlerStub = stub();
  969. const NOW = Date.now();
  970. inheritAttributes(stringToMpdXml(`
  971. <MPD type="dyanmic">
  972. <ContentSteering defaultServiceLocation="alpha" queryBeforeStart="false" proxyServerURL="http://127.0.0.1:3455/steer">https://example.com/app/url</ContentSteering>
  973. <ContentSteering defaultServiceLocation="beta" queryBeforeStart="false" proxyServerURL="http://127.0.0.1:3455/steer">https://example.com/app/url</ContentSteering>
  974. <BaseURL serviceLocation="alpha">https://cdn1.example.com/</BaseURL>
  975. <BaseURL serviceLocation="beta">https://cdn2.example.com/</BaseURL>
  976. <Period start="PT0S">
  977. <AdaptationSet mimeType="video/mp4">
  978. <Role value="main"></Role>
  979. <SegmentTemplate></SegmentTemplate>
  980. <Representation
  981. bandwidth="5000000"
  982. codecs="avc1.64001e"
  983. height="404"
  984. id="test"
  985. width="720">
  986. </Representation>
  987. </AdaptationSet>
  988. <AdaptationSet mimeType="text/vtt" lang="en">
  989. <Representation bandwidth="256" id="en">
  990. <BaseURL>/video</BaseURL>
  991. </Representation>
  992. </AdaptationSet>
  993. </Period>
  994. </MPD>
  995. `), { NOW, manifestUri: 'https://www.test.com', eventHandler: handlerStub });
  996. assert.ok(handlerStub.calledWith({
  997. type: 'warn',
  998. message: 'The MPD manifest should contain no more than one ContentSteering tag'
  999. }));
  1000. });
  1001. QUnit.test('end to end - basic multiperiod', function(assert) {
  1002. const NOW = Date.now();
  1003. // no start time attributes on either period, should be inferred
  1004. const actual = inheritAttributes(stringToMpdXml(`
  1005. <MPD mediaPresentationDuration="PT60S" >
  1006. <BaseURL>https://www.example.com/base/</BaseURL>
  1007. <Period duration="PT30S">
  1008. <AdaptationSet mimeType="video/mp4" >
  1009. <Role value="main"></Role>
  1010. <SegmentTemplate></SegmentTemplate>
  1011. <Representation
  1012. bandwidth="5000000"
  1013. codecs="avc1.64001e"
  1014. height="404"
  1015. id="test"
  1016. width="720">
  1017. </Representation>
  1018. </AdaptationSet>
  1019. </Period>
  1020. <Period>
  1021. <AdaptationSet mimeType="video/mp4" >
  1022. <Role value="main"></Role>
  1023. <SegmentTemplate></SegmentTemplate>
  1024. <Representation
  1025. bandwidth="5000000"
  1026. codecs="avc1.64001e"
  1027. height="404"
  1028. id="test"
  1029. width="720">
  1030. </Representation>
  1031. </AdaptationSet>
  1032. </Period>
  1033. </MPD>
  1034. `), { NOW });
  1035. const expected = {
  1036. contentSteeringInfo: null,
  1037. eventStream: [],
  1038. locations: undefined,
  1039. representationInfo: [{
  1040. attributes: {
  1041. bandwidth: 5000000,
  1042. baseUrl: 'https://www.example.com/base/',
  1043. codecs: 'avc1.64001e',
  1044. height: 404,
  1045. id: 'test',
  1046. mediaPresentationDuration: 60,
  1047. mimeType: 'video/mp4',
  1048. periodDuration: 30,
  1049. // inferred start
  1050. periodStart: 0,
  1051. role: {
  1052. value: 'main'
  1053. },
  1054. sourceDuration: 60,
  1055. type: 'static',
  1056. width: 720,
  1057. NOW,
  1058. clientOffset: 0
  1059. },
  1060. segmentInfo: {
  1061. template: {}
  1062. }
  1063. }, {
  1064. attributes: {
  1065. bandwidth: 5000000,
  1066. baseUrl: 'https://www.example.com/base/',
  1067. codecs: 'avc1.64001e',
  1068. height: 404,
  1069. id: 'test',
  1070. mediaPresentationDuration: 60,
  1071. mimeType: 'video/mp4',
  1072. // inferred start
  1073. periodStart: 30,
  1074. role: {
  1075. value: 'main'
  1076. },
  1077. sourceDuration: 60,
  1078. type: 'static',
  1079. width: 720,
  1080. NOW,
  1081. clientOffset: 0
  1082. },
  1083. segmentInfo: {
  1084. template: {}
  1085. }
  1086. }]
  1087. };
  1088. assert.equal(actual.representationInfo.length, 2);
  1089. assert.deepEqual(actual, expected);
  1090. });
  1091. QUnit.test('end to end - inherits BaseURL from all levels', function(assert) {
  1092. const NOW = Date.now();
  1093. const actual = inheritAttributes(stringToMpdXml(`
  1094. <MPD mediaPresentationDuration="PT30S" >
  1095. <BaseURL>https://www.example.com/base/</BaseURL>
  1096. <Period>
  1097. <BaseURL>foo/</BaseURL>
  1098. <AdaptationSet mimeType="video/mp4" >
  1099. <BaseURL>bar/</BaseURL>
  1100. <Role value="main"></Role>
  1101. <SegmentTemplate></SegmentTemplate>
  1102. <Representation
  1103. bandwidth="5000000"
  1104. codecs="avc1.64001e"
  1105. height="404"
  1106. id="test"
  1107. width="720">
  1108. <BaseURL>buzz/</BaseURL>
  1109. </Representation>
  1110. </AdaptationSet>
  1111. <AdaptationSet mimeType="text/vtt" lang="en">
  1112. <Representation bandwidth="256" id="en">
  1113. <BaseURL>https://example.com/en.vtt</BaseURL>
  1114. </Representation>
  1115. </AdaptationSet>
  1116. </Period>
  1117. </MPD>
  1118. `), { NOW });
  1119. const expected = {
  1120. contentSteeringInfo: null,
  1121. eventStream: [],
  1122. locations: undefined,
  1123. representationInfo: [{
  1124. attributes: {
  1125. bandwidth: 5000000,
  1126. baseUrl: 'https://www.example.com/base/foo/bar/buzz/',
  1127. clientOffset: 0,
  1128. codecs: 'avc1.64001e',
  1129. height: 404,
  1130. id: 'test',
  1131. mediaPresentationDuration: 30,
  1132. mimeType: 'video/mp4',
  1133. periodStart: 0,
  1134. role: {
  1135. value: 'main'
  1136. },
  1137. sourceDuration: 30,
  1138. type: 'static',
  1139. width: 720,
  1140. NOW
  1141. },
  1142. segmentInfo: {
  1143. template: {}
  1144. }
  1145. }, {
  1146. attributes: {
  1147. bandwidth: 256,
  1148. baseUrl: 'https://example.com/en.vtt',
  1149. id: 'en',
  1150. lang: 'en',
  1151. mediaPresentationDuration: 30,
  1152. mimeType: 'text/vtt',
  1153. periodStart: 0,
  1154. role: {},
  1155. sourceDuration: 30,
  1156. type: 'static',
  1157. NOW,
  1158. clientOffset: 0
  1159. },
  1160. segmentInfo: { }
  1161. }]
  1162. };
  1163. assert.equal(actual.representationInfo.length, 2);
  1164. assert.deepEqual(actual, expected);
  1165. });
  1166. QUnit.test('end to end - alternate BaseURLs', function(assert) {
  1167. const NOW = Date.now();
  1168. const actual = inheritAttributes(stringToMpdXml(`
  1169. <MPD mediaPresentationDuration= "PT30S" >
  1170. <BaseURL>https://www.example.com/base/</BaseURL>
  1171. <BaseURL>https://www.test.com/base/</BaseURL>
  1172. <Period>
  1173. <AdaptationSet mimeType= "video/mp4" >
  1174. <BaseURL>segments/</BaseURL>
  1175. <BaseURL>media/</BaseURL>
  1176. <Role value= "main" ></Role>
  1177. <SegmentTemplate></SegmentTemplate>
  1178. <Representation
  1179. bandwidth= "5000000"
  1180. codecs= "avc1.64001e"
  1181. height= "404"
  1182. id= "test"
  1183. width= "720" >
  1184. </Representation>
  1185. </AdaptationSet>
  1186. <AdaptationSet mimeType= "text/vtt" lang= "en" >
  1187. <Representation bandwidth= "256" id= "en" >
  1188. <BaseURL>https://example.com/en.vtt</BaseURL>
  1189. </Representation>
  1190. </AdaptationSet>
  1191. </Period>
  1192. </MPD>
  1193. `), { NOW });
  1194. const expected = {
  1195. contentSteeringInfo: null,
  1196. eventStream: [],
  1197. locations: undefined,
  1198. representationInfo: [{
  1199. attributes: {
  1200. bandwidth: 5000000,
  1201. baseUrl: 'https://www.example.com/base/segments/',
  1202. codecs: 'avc1.64001e',
  1203. height: 404,
  1204. id: 'test',
  1205. mediaPresentationDuration: 30,
  1206. mimeType: 'video/mp4',
  1207. periodStart: 0,
  1208. role: {
  1209. value: 'main'
  1210. },
  1211. sourceDuration: 30,
  1212. type: 'static',
  1213. width: 720,
  1214. NOW,
  1215. clientOffset: 0
  1216. },
  1217. segmentInfo: {
  1218. template: {}
  1219. }
  1220. }, {
  1221. attributes: {
  1222. bandwidth: 5000000,
  1223. baseUrl: 'https://www.example.com/base/media/',
  1224. codecs: 'avc1.64001e',
  1225. height: 404,
  1226. id: 'test',
  1227. mediaPresentationDuration: 30,
  1228. mimeType: 'video/mp4',
  1229. periodStart: 0,
  1230. role: {
  1231. value: 'main'
  1232. },
  1233. sourceDuration: 30,
  1234. type: 'static',
  1235. width: 720,
  1236. NOW,
  1237. clientOffset: 0
  1238. },
  1239. segmentInfo: {
  1240. template: {}
  1241. }
  1242. }, {
  1243. attributes: {
  1244. bandwidth: 5000000,
  1245. baseUrl: 'https://www.test.com/base/segments/',
  1246. codecs: 'avc1.64001e',
  1247. height: 404,
  1248. id: 'test',
  1249. mediaPresentationDuration: 30,
  1250. mimeType: 'video/mp4',
  1251. periodStart: 0,
  1252. role: {
  1253. value: 'main'
  1254. },
  1255. sourceDuration: 30,
  1256. type: 'static',
  1257. width: 720,
  1258. NOW,
  1259. clientOffset: 0
  1260. },
  1261. segmentInfo: {
  1262. template: {}
  1263. }
  1264. }, {
  1265. attributes: {
  1266. bandwidth: 5000000,
  1267. baseUrl: 'https://www.test.com/base/media/',
  1268. codecs: 'avc1.64001e',
  1269. height: 404,
  1270. id: 'test',
  1271. mediaPresentationDuration: 30,
  1272. mimeType: 'video/mp4',
  1273. periodStart: 0,
  1274. role: {
  1275. value: 'main'
  1276. },
  1277. sourceDuration: 30,
  1278. type: 'static',
  1279. width: 720,
  1280. NOW,
  1281. clientOffset: 0
  1282. },
  1283. segmentInfo: {
  1284. template: {}
  1285. }
  1286. }, {
  1287. attributes: {
  1288. bandwidth: 256,
  1289. baseUrl: 'https://example.com/en.vtt',
  1290. id: 'en',
  1291. lang: 'en',
  1292. mediaPresentationDuration: 30,
  1293. mimeType: 'text/vtt',
  1294. periodStart: 0,
  1295. role: {},
  1296. sourceDuration: 30,
  1297. type: 'static',
  1298. NOW,
  1299. clientOffset: 0
  1300. },
  1301. segmentInfo: {}
  1302. }, {
  1303. attributes: {
  1304. bandwidth: 256,
  1305. baseUrl: 'https://example.com/en.vtt',
  1306. id: 'en',
  1307. lang: 'en',
  1308. mediaPresentationDuration: 30,
  1309. mimeType: 'text/vtt',
  1310. periodStart: 0,
  1311. role: {},
  1312. sourceDuration: 30,
  1313. type: 'static',
  1314. NOW,
  1315. clientOffset: 0
  1316. },
  1317. segmentInfo: {}
  1318. }]
  1319. };
  1320. assert.equal(actual.representationInfo.length, 6);
  1321. assert.deepEqual(actual, expected);
  1322. });
  1323. QUnit.test(
  1324. ' End to End test for checking support of segments in representation',
  1325. function(assert) {
  1326. const NOW = Date.now();
  1327. const actual = inheritAttributes(stringToMpdXml(`
  1328. <MPD mediaPresentationDuration= "PT30S" >
  1329. <BaseURL>https://www.example.com/base/</BaseURL>
  1330. <Period>
  1331. <AdaptationSet mimeType= "video/mp4" >
  1332. <Role value= "main" ></Role>
  1333. <SegmentBase indexRangeExact= "true" indexRange= "820-2087" >
  1334. <Initialization range= "0-987" />
  1335. </SegmentBase>
  1336. <Representation
  1337. mimeType= "video/mp6"
  1338. bandwidth= "5000000"
  1339. codecs= "avc1.64001e"
  1340. height= "404"
  1341. id= "test"
  1342. width= "720" >
  1343. <SegmentBase>
  1344. <Initialization range= "0-567" />
  1345. </SegmentBase>
  1346. </Representation>
  1347. <Representation
  1348. height= "545" >
  1349. </Representation>
  1350. </AdaptationSet>
  1351. <AdaptationSet mimeType= "text/vtt" lang= "en" >
  1352. <Representation bandwidth= "256" id= "en" >
  1353. <BaseURL>https://example.com/en.vtt</BaseURL>
  1354. </Representation>
  1355. </AdaptationSet>
  1356. </Period>
  1357. </MPD>
  1358. `), { NOW });
  1359. const expected = {
  1360. contentSteeringInfo: null,
  1361. eventStream: [],
  1362. locations: undefined,
  1363. representationInfo: [{
  1364. attributes: {
  1365. bandwidth: 5000000,
  1366. baseUrl: 'https://www.example.com/base/',
  1367. codecs: 'avc1.64001e',
  1368. height: 404,
  1369. id: 'test',
  1370. mediaPresentationDuration: 30,
  1371. mimeType: 'video/mp6',
  1372. periodStart: 0,
  1373. role: {
  1374. value: 'main'
  1375. },
  1376. sourceDuration: 30,
  1377. type: 'static',
  1378. width: 720,
  1379. NOW,
  1380. clientOffset: 0
  1381. },
  1382. segmentInfo: {
  1383. base: {
  1384. indexRange: '820-2087',
  1385. indexRangeExact: 'true',
  1386. initialization: {
  1387. range: '0-567'
  1388. }
  1389. }
  1390. }
  1391. }, {
  1392. attributes: {
  1393. baseUrl: 'https://www.example.com/base/',
  1394. mediaPresentationDuration: 30,
  1395. mimeType: 'video/mp4',
  1396. periodStart: 0,
  1397. height: 545,
  1398. role: {
  1399. value: 'main'
  1400. },
  1401. sourceDuration: 30,
  1402. type: 'static',
  1403. NOW,
  1404. clientOffset: 0
  1405. },
  1406. segmentInfo: {
  1407. base: {
  1408. indexRange: '820-2087',
  1409. indexRangeExact: 'true',
  1410. initialization: {
  1411. range: '0-987'
  1412. }
  1413. }
  1414. }
  1415. }, {
  1416. attributes: {
  1417. bandwidth: 256,
  1418. baseUrl: 'https://example.com/en.vtt',
  1419. id: 'en',
  1420. lang: 'en',
  1421. mediaPresentationDuration: 30,
  1422. mimeType: 'text/vtt',
  1423. periodStart: 0,
  1424. role: {},
  1425. sourceDuration: 30,
  1426. type: 'static',
  1427. NOW,
  1428. clientOffset: 0
  1429. },
  1430. segmentInfo: {}
  1431. }]
  1432. };
  1433. assert.equal(actual.representationInfo.length, 3);
  1434. assert.deepEqual(actual, expected);
  1435. }
  1436. );
  1437. QUnit.test(
  1438. ' End to End test for checking support of segments in period ',
  1439. function(assert) {
  1440. const NOW = Date.now();
  1441. const actual = inheritAttributes(stringToMpdXml(`
  1442. <MPD mediaPresentationDuration= "PT30S" >
  1443. <BaseURL>https://www.example.com/base/</BaseURL>
  1444. <Period duration= "PT0H4M40.414S" >
  1445. <SegmentBase indexRangeExact= "false" indexRange= "9999" >
  1446. <Initialization range= "0-1111" />
  1447. </SegmentBase>
  1448. <AdaptationSet mimeType= "video/mp4" >
  1449. <Role value= "main" ></Role>
  1450. <Representation
  1451. mimeType= "video/mp6"
  1452. bandwidth= "5000000"
  1453. codecs= "avc1.64001e"
  1454. height= "404"
  1455. id= "test"
  1456. width= "720" >
  1457. </Representation>
  1458. <Representation
  1459. height= "545" >
  1460. </Representation>
  1461. </AdaptationSet>
  1462. <AdaptationSet mimeType= "text/vtt" lang= "en" >
  1463. <Representation bandwidth= "256" id= "en" >
  1464. <BaseURL>https://example.com/en.vtt</BaseURL>
  1465. </Representation>
  1466. </AdaptationSet>
  1467. </Period>
  1468. </MPD>
  1469. `), { NOW });
  1470. const expected = {
  1471. contentSteeringInfo: null,
  1472. eventStream: [],
  1473. locations: undefined,
  1474. representationInfo: [{
  1475. attributes: {
  1476. bandwidth: 5000000,
  1477. baseUrl: 'https://www.example.com/base/',
  1478. codecs: 'avc1.64001e',
  1479. height: 404,
  1480. id: 'test',
  1481. mediaPresentationDuration: 30,
  1482. mimeType: 'video/mp6',
  1483. periodDuration: 280.414,
  1484. periodStart: 0,
  1485. role: {
  1486. value: 'main'
  1487. },
  1488. sourceDuration: 30,
  1489. type: 'static',
  1490. width: 720,
  1491. NOW,
  1492. clientOffset: 0
  1493. },
  1494. segmentInfo: {
  1495. base: {
  1496. indexRange: '9999',
  1497. indexRangeExact: 'false',
  1498. initialization: {
  1499. range: '0-1111'
  1500. }
  1501. }
  1502. }
  1503. }, {
  1504. attributes: {
  1505. baseUrl: 'https://www.example.com/base/',
  1506. mediaPresentationDuration: 30,
  1507. mimeType: 'video/mp4',
  1508. periodDuration: 280.414,
  1509. periodStart: 0,
  1510. height: 545,
  1511. role: {
  1512. value: 'main'
  1513. },
  1514. sourceDuration: 30,
  1515. type: 'static',
  1516. NOW,
  1517. clientOffset: 0
  1518. },
  1519. segmentInfo: {
  1520. base: {
  1521. indexRange: '9999',
  1522. indexRangeExact: 'false',
  1523. initialization: {
  1524. range: '0-1111'
  1525. }
  1526. }
  1527. }
  1528. }, {
  1529. attributes: {
  1530. bandwidth: 256,
  1531. baseUrl: 'https://example.com/en.vtt',
  1532. id: 'en',
  1533. lang: 'en',
  1534. mediaPresentationDuration: 30,
  1535. mimeType: 'text/vtt',
  1536. periodDuration: 280.414,
  1537. periodStart: 0,
  1538. role: {},
  1539. sourceDuration: 30,
  1540. type: 'static',
  1541. NOW,
  1542. clientOffset: 0
  1543. },
  1544. segmentInfo: {
  1545. base: {
  1546. indexRange: '9999',
  1547. indexRangeExact: 'false',
  1548. initialization: {
  1549. range: '0-1111'
  1550. }
  1551. }
  1552. }
  1553. }]
  1554. };
  1555. assert.equal(actual.representationInfo.length, 3);
  1556. assert.deepEqual(actual, expected);
  1557. }
  1558. );
  1559. QUnit.test(
  1560. ' End to End test for checking support of Segments in Adaptation set',
  1561. function(assert) {
  1562. const NOW = Date.now();
  1563. const actual = inheritAttributes(stringToMpdXml(`
  1564. <MPD mediaPresentationDuration= "PT30S" >
  1565. <BaseURL>https://www.example.com/base/</BaseURL>
  1566. <Period>
  1567. <AdaptationSet mimeType= "video/mp4" >
  1568. <Role value= "main" ></Role>
  1569. <SegmentBase indexRange= "1212" indexRangeExact= "true" >
  1570. <Initialization range= "0-8888" />
  1571. </SegmentBase>
  1572. <Representation
  1573. mimeType= "video/mp6"
  1574. bandwidth= "5000000"
  1575. codecs= "avc1.64001e"
  1576. height= "404"
  1577. id= "test"
  1578. width= "720" >
  1579. </Representation>
  1580. <Representation
  1581. height= "545" >
  1582. </Representation>
  1583. </AdaptationSet>
  1584. <AdaptationSet mimeType= "text/vtt" lang= "en" >
  1585. <Representation bandwidth= "256" id= "en" >
  1586. <BaseURL>https://example.com/en.vtt</BaseURL>
  1587. </Representation>
  1588. </AdaptationSet>
  1589. </Period>
  1590. </MPD>
  1591. `), { NOW });
  1592. const expected = {
  1593. contentSteeringInfo: null,
  1594. eventStream: [],
  1595. locations: undefined,
  1596. representationInfo: [{
  1597. attributes: {
  1598. bandwidth: 5000000,
  1599. baseUrl: 'https://www.example.com/base/',
  1600. codecs: 'avc1.64001e',
  1601. height: 404,
  1602. id: 'test',
  1603. mediaPresentationDuration: 30,
  1604. mimeType: 'video/mp6',
  1605. periodStart: 0,
  1606. role: {
  1607. value: 'main'
  1608. },
  1609. sourceDuration: 30,
  1610. type: 'static',
  1611. width: 720,
  1612. NOW,
  1613. clientOffset: 0
  1614. },
  1615. segmentInfo: {
  1616. base: {
  1617. indexRange: '1212',
  1618. indexRangeExact: 'true',
  1619. initialization: {
  1620. range: '0-8888'
  1621. }
  1622. }
  1623. }
  1624. }, {
  1625. attributes: {
  1626. baseUrl: 'https://www.example.com/base/',
  1627. mediaPresentationDuration: 30,
  1628. mimeType: 'video/mp4',
  1629. periodStart: 0,
  1630. height: 545,
  1631. role: {
  1632. value: 'main'
  1633. },
  1634. sourceDuration: 30,
  1635. type: 'static',
  1636. NOW,
  1637. clientOffset: 0
  1638. },
  1639. segmentInfo: {
  1640. base: {
  1641. indexRange: '1212',
  1642. indexRangeExact: 'true',
  1643. initialization: {
  1644. range: '0-8888'
  1645. }
  1646. }
  1647. }
  1648. }, {
  1649. attributes: {
  1650. bandwidth: 256,
  1651. baseUrl: 'https://example.com/en.vtt',
  1652. id: 'en',
  1653. lang: 'en',
  1654. mediaPresentationDuration: 30,
  1655. mimeType: 'text/vtt',
  1656. periodStart: 0,
  1657. role: {},
  1658. sourceDuration: 30,
  1659. type: 'static',
  1660. NOW,
  1661. clientOffset: 0
  1662. },
  1663. segmentInfo: {}
  1664. }]
  1665. };
  1666. assert.equal(actual.representationInfo.length, 3);
  1667. assert.deepEqual(actual, expected);
  1668. }
  1669. );
  1670. // Although according to the Spec, at most one set of Segment information should be
  1671. // present at each level, this test would still handle the case and prevent errors if
  1672. // multiple set of segment information are present at any particular level.
  1673. QUnit.test(
  1674. 'Test for checking use of only one set of Segment Information when multiple are present',
  1675. function(assert) {
  1676. const NOW = Date.now();
  1677. const actual = toPlaylists(inheritAttributes(stringToMpdXml(`
  1678. <MPD mediaPresentationDuration= "PT30S" >
  1679. <BaseURL>https://www.example.com/base</BaseURL>
  1680. <Period>
  1681. <AdaptationSet
  1682. mimeType= "video/mp4"
  1683. segmentAlignment= "true"
  1684. startWithSAP= "1"
  1685. lang= "es" >
  1686. <Role value= "main" ></Role>
  1687. <SegmentTemplate
  1688. duration= "95232"
  1689. initialization= "$RepresentationID$/es/init.m4f"
  1690. media= "$RepresentationID$/es/$Number$.m4f"
  1691. startNumber= "0"
  1692. timescale= "48000" >
  1693. </SegmentTemplate>
  1694. <SegmentList timescale= "1000" duration= "1000" >
  1695. <RepresentationIndex sourceURL= "representation-index-low" />
  1696. <SegmentURL media= "low/segment-1.ts" />
  1697. <SegmentURL media= "low/segment-2.ts" />
  1698. <SegmentURL media= "low/segment-3.ts" />
  1699. <SegmentURL media= "low/segment-4.ts" />
  1700. <SegmentURL media= "low/segment-5.ts" />
  1701. <SegmentURL media= "low/segment-6.ts" />
  1702. </SegmentList>
  1703. <Representation
  1704. mimeType= "video/mp6"
  1705. bandwidth= "5000000"
  1706. codecs= "avc1.64001e"
  1707. height= "404"
  1708. id= "125000"
  1709. width= "720" >
  1710. </Representation>
  1711. <Representation
  1712. height= "545"
  1713. id="125000" >
  1714. </Representation>
  1715. </AdaptationSet>
  1716. </Period>
  1717. </MPD>
  1718. `), { NOW }).representationInfo);
  1719. const expected = [{
  1720. attributes: {
  1721. NOW,
  1722. bandwidth: 5000000,
  1723. baseUrl: 'https://www.example.com/base',
  1724. duration: 1.984,
  1725. codecs: 'avc1.64001e',
  1726. height: 404,
  1727. id: '125000',
  1728. lang: 'es',
  1729. mediaPresentationDuration: 30,
  1730. mimeType: 'video/mp6',
  1731. periodStart: 0,
  1732. startNumber: 0,
  1733. timescale: 48000,
  1734. role: {
  1735. value: 'main'
  1736. },
  1737. clientOffset: 0,
  1738. initialization: {
  1739. sourceURL: '$RepresentationID$/es/init.m4f'
  1740. },
  1741. media: '$RepresentationID$/es/$Number$.m4f',
  1742. segmentAlignment: 'true',
  1743. sourceDuration: 30,
  1744. type: 'static',
  1745. width: 720,
  1746. startWithSAP: '1'
  1747. },
  1748. segments: [{
  1749. duration: 1.984,
  1750. map: {
  1751. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  1752. uri: '125000/es/init.m4f'
  1753. },
  1754. resolvedUri: 'https://www.example.com/125000/es/0.m4f',
  1755. timeline: 0,
  1756. uri: '125000/es/0.m4f',
  1757. number: 0,
  1758. presentationTime: 0
  1759. }, {
  1760. duration: 1.984,
  1761. map: {
  1762. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  1763. uri: '125000/es/init.m4f'
  1764. },
  1765. resolvedUri: 'https://www.example.com/125000/es/1.m4f',
  1766. timeline: 0,
  1767. uri: '125000/es/1.m4f',
  1768. number: 1,
  1769. presentationTime: 1.984
  1770. }, {
  1771. duration: 1.984,
  1772. map: {
  1773. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  1774. uri: '125000/es/init.m4f'
  1775. },
  1776. resolvedUri: 'https://www.example.com/125000/es/2.m4f',
  1777. timeline: 0,
  1778. uri: '125000/es/2.m4f',
  1779. number: 2,
  1780. presentationTime: 3.968
  1781. }, {
  1782. duration: 1.984,
  1783. map: {
  1784. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  1785. uri: '125000/es/init.m4f'
  1786. },
  1787. resolvedUri: 'https://www.example.com/125000/es/3.m4f',
  1788. timeline: 0,
  1789. uri: '125000/es/3.m4f',
  1790. number: 3,
  1791. presentationTime: 5.952
  1792. }, {
  1793. duration: 1.984,
  1794. map: {
  1795. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  1796. uri: '125000/es/init.m4f'
  1797. },
  1798. resolvedUri: 'https://www.example.com/125000/es/4.m4f',
  1799. timeline: 0,
  1800. uri: '125000/es/4.m4f',
  1801. number: 4,
  1802. presentationTime: 7.936
  1803. }, {
  1804. duration: 1.984,
  1805. map: {
  1806. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  1807. uri: '125000/es/init.m4f'
  1808. },
  1809. resolvedUri: 'https://www.example.com/125000/es/5.m4f',
  1810. timeline: 0,
  1811. uri: '125000/es/5.m4f',
  1812. number: 5,
  1813. presentationTime: 9.92
  1814. }, {
  1815. duration: 1.984,
  1816. map: {
  1817. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  1818. uri: '125000/es/init.m4f'
  1819. },
  1820. resolvedUri: 'https://www.example.com/125000/es/6.m4f',
  1821. timeline: 0,
  1822. uri: '125000/es/6.m4f',
  1823. number: 6,
  1824. presentationTime: 11.904
  1825. }, {
  1826. duration: 1.984,
  1827. map: {
  1828. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  1829. uri: '125000/es/init.m4f'
  1830. },
  1831. resolvedUri: 'https://www.example.com/125000/es/7.m4f',
  1832. timeline: 0,
  1833. uri: '125000/es/7.m4f',
  1834. number: 7,
  1835. presentationTime: 13.888
  1836. }, {
  1837. duration: 1.984,
  1838. map: {
  1839. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  1840. uri: '125000/es/init.m4f'
  1841. },
  1842. resolvedUri: 'https://www.example.com/125000/es/8.m4f',
  1843. timeline: 0,
  1844. uri: '125000/es/8.m4f',
  1845. number: 8,
  1846. presentationTime: 15.872
  1847. }, {
  1848. duration: 1.984,
  1849. map: {
  1850. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  1851. uri: '125000/es/init.m4f'
  1852. },
  1853. resolvedUri: 'https://www.example.com/125000/es/9.m4f',
  1854. timeline: 0,
  1855. uri: '125000/es/9.m4f',
  1856. number: 9,
  1857. presentationTime: 17.856
  1858. }, {
  1859. duration: 1.984,
  1860. map: {
  1861. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  1862. uri: '125000/es/init.m4f'
  1863. },
  1864. resolvedUri: 'https://www.example.com/125000/es/10.m4f',
  1865. timeline: 0,
  1866. uri: '125000/es/10.m4f',
  1867. number: 10,
  1868. presentationTime: 19.84
  1869. }, {
  1870. duration: 1.984,
  1871. map: {
  1872. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  1873. uri: '125000/es/init.m4f'
  1874. },
  1875. resolvedUri: 'https://www.example.com/125000/es/11.m4f',
  1876. timeline: 0,
  1877. uri: '125000/es/11.m4f',
  1878. number: 11,
  1879. presentationTime: 21.824
  1880. }, {
  1881. duration: 1.984,
  1882. map: {
  1883. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  1884. uri: '125000/es/init.m4f'
  1885. },
  1886. resolvedUri: 'https://www.example.com/125000/es/12.m4f',
  1887. timeline: 0,
  1888. uri: '125000/es/12.m4f',
  1889. number: 12,
  1890. presentationTime: 23.808
  1891. }, {
  1892. duration: 1.984,
  1893. map: {
  1894. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  1895. uri: '125000/es/init.m4f'
  1896. },
  1897. resolvedUri: 'https://www.example.com/125000/es/13.m4f',
  1898. timeline: 0,
  1899. uri: '125000/es/13.m4f',
  1900. number: 13,
  1901. presentationTime: 25.792
  1902. }, {
  1903. duration: 1.984,
  1904. map: {
  1905. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  1906. uri: '125000/es/init.m4f'
  1907. },
  1908. resolvedUri: 'https://www.example.com/125000/es/14.m4f',
  1909. timeline: 0,
  1910. uri: '125000/es/14.m4f',
  1911. number: 14,
  1912. presentationTime: 27.776
  1913. }, {
  1914. duration: 0.240000000000002,
  1915. map: {
  1916. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  1917. uri: '125000/es/init.m4f'
  1918. },
  1919. resolvedUri: 'https://www.example.com/125000/es/15.m4f',
  1920. timeline: 0,
  1921. uri: '125000/es/15.m4f',
  1922. number: 15,
  1923. presentationTime: 29.76
  1924. }]
  1925. }, {
  1926. attributes: {
  1927. NOW,
  1928. baseUrl: 'https://www.example.com/base',
  1929. duration: 1.984,
  1930. lang: 'es',
  1931. height: 545,
  1932. id: '125000',
  1933. mediaPresentationDuration: 30,
  1934. mimeType: 'video/mp4',
  1935. periodStart: 0,
  1936. role: {
  1937. value: 'main'
  1938. },
  1939. segmentAlignment: 'true',
  1940. sourceDuration: 30,
  1941. type: 'static',
  1942. startWithSAP: '1',
  1943. clientOffset: 0,
  1944. initialization: {
  1945. sourceURL: '$RepresentationID$/es/init.m4f'
  1946. },
  1947. media: '$RepresentationID$/es/$Number$.m4f',
  1948. startNumber: 0,
  1949. timescale: 48000
  1950. },
  1951. segments: [{
  1952. duration: 1.984,
  1953. map: {
  1954. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  1955. uri: '125000/es/init.m4f'
  1956. },
  1957. resolvedUri: 'https://www.example.com/125000/es/0.m4f',
  1958. timeline: 0,
  1959. uri: '125000/es/0.m4f',
  1960. number: 0,
  1961. presentationTime: 0
  1962. }, {
  1963. duration: 1.984,
  1964. map: {
  1965. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  1966. uri: '125000/es/init.m4f'
  1967. },
  1968. resolvedUri: 'https://www.example.com/125000/es/1.m4f',
  1969. timeline: 0,
  1970. uri: '125000/es/1.m4f',
  1971. number: 1,
  1972. presentationTime: 1.984
  1973. }, {
  1974. duration: 1.984,
  1975. map: {
  1976. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  1977. uri: '125000/es/init.m4f'
  1978. },
  1979. resolvedUri: 'https://www.example.com/125000/es/2.m4f',
  1980. timeline: 0,
  1981. uri: '125000/es/2.m4f',
  1982. number: 2,
  1983. presentationTime: 3.968
  1984. }, {
  1985. duration: 1.984,
  1986. map: {
  1987. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  1988. uri: '125000/es/init.m4f'
  1989. },
  1990. resolvedUri: 'https://www.example.com/125000/es/3.m4f',
  1991. timeline: 0,
  1992. uri: '125000/es/3.m4f',
  1993. number: 3,
  1994. presentationTime: 5.952
  1995. }, {
  1996. duration: 1.984,
  1997. map: {
  1998. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  1999. uri: '125000/es/init.m4f'
  2000. },
  2001. resolvedUri: 'https://www.example.com/125000/es/4.m4f',
  2002. timeline: 0,
  2003. uri: '125000/es/4.m4f',
  2004. number: 4,
  2005. presentationTime: 7.936
  2006. }, {
  2007. duration: 1.984,
  2008. map: {
  2009. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  2010. uri: '125000/es/init.m4f'
  2011. },
  2012. resolvedUri: 'https://www.example.com/125000/es/5.m4f',
  2013. timeline: 0,
  2014. uri: '125000/es/5.m4f',
  2015. number: 5,
  2016. presentationTime: 9.92
  2017. }, {
  2018. duration: 1.984,
  2019. map: {
  2020. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  2021. uri: '125000/es/init.m4f'
  2022. },
  2023. resolvedUri: 'https://www.example.com/125000/es/6.m4f',
  2024. timeline: 0,
  2025. uri: '125000/es/6.m4f',
  2026. number: 6,
  2027. presentationTime: 11.904
  2028. }, {
  2029. duration: 1.984,
  2030. map: {
  2031. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  2032. uri: '125000/es/init.m4f'
  2033. },
  2034. resolvedUri: 'https://www.example.com/125000/es/7.m4f',
  2035. timeline: 0,
  2036. uri: '125000/es/7.m4f',
  2037. number: 7,
  2038. presentationTime: 13.888
  2039. }, {
  2040. duration: 1.984,
  2041. map: {
  2042. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  2043. uri: '125000/es/init.m4f'
  2044. },
  2045. resolvedUri: 'https://www.example.com/125000/es/8.m4f',
  2046. timeline: 0,
  2047. uri: '125000/es/8.m4f',
  2048. number: 8,
  2049. presentationTime: 15.872
  2050. }, {
  2051. duration: 1.984,
  2052. map: {
  2053. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  2054. uri: '125000/es/init.m4f'
  2055. },
  2056. resolvedUri: 'https://www.example.com/125000/es/9.m4f',
  2057. timeline: 0,
  2058. uri: '125000/es/9.m4f',
  2059. number: 9,
  2060. presentationTime: 17.856
  2061. }, {
  2062. duration: 1.984,
  2063. map: {
  2064. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  2065. uri: '125000/es/init.m4f'
  2066. },
  2067. resolvedUri: 'https://www.example.com/125000/es/10.m4f',
  2068. timeline: 0,
  2069. uri: '125000/es/10.m4f',
  2070. number: 10,
  2071. presentationTime: 19.84
  2072. }, {
  2073. duration: 1.984,
  2074. map: {
  2075. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  2076. uri: '125000/es/init.m4f'
  2077. },
  2078. resolvedUri: 'https://www.example.com/125000/es/11.m4f',
  2079. timeline: 0,
  2080. uri: '125000/es/11.m4f',
  2081. number: 11,
  2082. presentationTime: 21.824
  2083. }, {
  2084. duration: 1.984,
  2085. map: {
  2086. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  2087. uri: '125000/es/init.m4f'
  2088. },
  2089. resolvedUri: 'https://www.example.com/125000/es/12.m4f',
  2090. timeline: 0,
  2091. uri: '125000/es/12.m4f',
  2092. number: 12,
  2093. presentationTime: 23.808
  2094. }, {
  2095. duration: 1.984,
  2096. map: {
  2097. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  2098. uri: '125000/es/init.m4f'
  2099. },
  2100. resolvedUri: 'https://www.example.com/125000/es/13.m4f',
  2101. timeline: 0,
  2102. uri: '125000/es/13.m4f',
  2103. number: 13,
  2104. presentationTime: 25.792
  2105. }, {
  2106. duration: 1.984,
  2107. map: {
  2108. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  2109. uri: '125000/es/init.m4f'
  2110. },
  2111. resolvedUri: 'https://www.example.com/125000/es/14.m4f',
  2112. timeline: 0,
  2113. uri: '125000/es/14.m4f',
  2114. number: 14,
  2115. presentationTime: 27.776
  2116. }, {
  2117. duration: 0.240000000000002,
  2118. map: {
  2119. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  2120. uri: '125000/es/init.m4f'
  2121. },
  2122. resolvedUri: 'https://www.example.com/125000/es/15.m4f',
  2123. timeline: 0,
  2124. uri: '125000/es/15.m4f',
  2125. number: 15,
  2126. presentationTime: 29.76
  2127. }]
  2128. }];
  2129. assert.equal(actual.length, 2);
  2130. assert.deepEqual(actual, expected);
  2131. }
  2132. );
  2133. // Although the Spec states that if SegmentTemplate or SegmentList is present on one
  2134. // level of the hierarchy the other one shall not be present on any lower level, this
  2135. // test would still handle the case if both are present in the hierarchy and would
  2136. // prevent throwing errors.
  2137. QUnit.test('Test to check use of either Segment Template or Segment List when both are' +
  2138. ' present in the hierarchy', function(assert) {
  2139. const NOW = Date.now();
  2140. const actual = toPlaylists(inheritAttributes(stringToMpdXml(`
  2141. <MPD mediaPresentationDuration= "PT30S" >
  2142. <BaseURL>https://www.example.com/base</BaseURL>
  2143. <Period>
  2144. <AdaptationSet
  2145. mimeType= "video/mp4"
  2146. segmentAlignment= "true"
  2147. startWithSAP= "1"
  2148. lang= "es" >
  2149. <Role value= "main" ></Role>
  2150. <SegmentTemplate
  2151. duration= "95232"
  2152. initialization= "$RepresentationID$/es/init.m4f"
  2153. media= "$RepresentationID$/es/$Number$.m4f"
  2154. startNumber= "0"
  2155. timescale= "48000" >
  2156. </SegmentTemplate>
  2157. <Representation
  2158. mimeType= "video/mp6"
  2159. bandwidth= "5000000"
  2160. codecs= "avc1.64001e"
  2161. height= "404"
  2162. id= "125000"
  2163. width= "720" >
  2164. <SegmentList timescale= "1000" duration= "1000" >
  2165. <RepresentationIndex sourceURL= "representation-index-low" />
  2166. <SegmentURL media= "low/segment-1.ts" />
  2167. <SegmentURL media= "low/segment-2.ts" />
  2168. <SegmentURL media= "low/segment-3.ts" />
  2169. <SegmentURL media= "low/segment-4.ts" />
  2170. <SegmentURL media= "low/segment-5.ts" />
  2171. <SegmentURL media= "low/segment-6.ts" />
  2172. </SegmentList>
  2173. </Representation>
  2174. </AdaptationSet>
  2175. </Period>
  2176. </MPD>
  2177. `), { NOW }).representationInfo);
  2178. const expected = [{
  2179. attributes: {
  2180. NOW,
  2181. clientOffset: 0,
  2182. initialization: {
  2183. sourceURL: '$RepresentationID$/es/init.m4f'
  2184. },
  2185. media: '$RepresentationID$/es/$Number$.m4f',
  2186. bandwidth: 5000000,
  2187. baseUrl: 'https://www.example.com/base',
  2188. duration: 1.984,
  2189. codecs: 'avc1.64001e',
  2190. height: 404,
  2191. id: '125000',
  2192. lang: 'es',
  2193. mediaPresentationDuration: 30,
  2194. mimeType: 'video/mp6',
  2195. periodStart: 0,
  2196. role: {
  2197. value: 'main'
  2198. },
  2199. segmentAlignment: 'true',
  2200. sourceDuration: 30,
  2201. type: 'static',
  2202. width: 720,
  2203. startWithSAP: '1',
  2204. startNumber: 0,
  2205. timescale: 48000
  2206. },
  2207. segments: [{
  2208. duration: 1.984,
  2209. map: {
  2210. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  2211. uri: '125000/es/init.m4f'
  2212. },
  2213. resolvedUri: 'https://www.example.com/125000/es/0.m4f',
  2214. timeline: 0,
  2215. uri: '125000/es/0.m4f',
  2216. number: 0,
  2217. presentationTime: 0
  2218. }, {
  2219. duration: 1.984,
  2220. map: {
  2221. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  2222. uri: '125000/es/init.m4f'
  2223. },
  2224. resolvedUri: 'https://www.example.com/125000/es/1.m4f',
  2225. timeline: 0,
  2226. uri: '125000/es/1.m4f',
  2227. number: 1,
  2228. presentationTime: 1.984
  2229. }, {
  2230. duration: 1.984,
  2231. map: {
  2232. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  2233. uri: '125000/es/init.m4f'
  2234. },
  2235. resolvedUri: 'https://www.example.com/125000/es/2.m4f',
  2236. timeline: 0,
  2237. uri: '125000/es/2.m4f',
  2238. number: 2,
  2239. presentationTime: 3.968
  2240. }, {
  2241. duration: 1.984,
  2242. map: {
  2243. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  2244. uri: '125000/es/init.m4f'
  2245. },
  2246. resolvedUri: 'https://www.example.com/125000/es/3.m4f',
  2247. timeline: 0,
  2248. uri: '125000/es/3.m4f',
  2249. number: 3,
  2250. presentationTime: 5.952
  2251. }, {
  2252. duration: 1.984,
  2253. map: {
  2254. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  2255. uri: '125000/es/init.m4f'
  2256. },
  2257. resolvedUri: 'https://www.example.com/125000/es/4.m4f',
  2258. timeline: 0,
  2259. uri: '125000/es/4.m4f',
  2260. number: 4,
  2261. presentationTime: 7.936
  2262. }, {
  2263. duration: 1.984,
  2264. map: {
  2265. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  2266. uri: '125000/es/init.m4f'
  2267. },
  2268. resolvedUri: 'https://www.example.com/125000/es/5.m4f',
  2269. timeline: 0,
  2270. uri: '125000/es/5.m4f',
  2271. number: 5,
  2272. presentationTime: 9.92
  2273. }, {
  2274. duration: 1.984,
  2275. map: {
  2276. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  2277. uri: '125000/es/init.m4f'
  2278. },
  2279. resolvedUri: 'https://www.example.com/125000/es/6.m4f',
  2280. timeline: 0,
  2281. uri: '125000/es/6.m4f',
  2282. number: 6,
  2283. presentationTime: 11.904
  2284. }, {
  2285. duration: 1.984,
  2286. map: {
  2287. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  2288. uri: '125000/es/init.m4f'
  2289. },
  2290. resolvedUri: 'https://www.example.com/125000/es/7.m4f',
  2291. timeline: 0,
  2292. uri: '125000/es/7.m4f',
  2293. number: 7,
  2294. presentationTime: 13.888
  2295. }, {
  2296. duration: 1.984,
  2297. map: {
  2298. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  2299. uri: '125000/es/init.m4f'
  2300. },
  2301. resolvedUri: 'https://www.example.com/125000/es/8.m4f',
  2302. timeline: 0,
  2303. uri: '125000/es/8.m4f',
  2304. number: 8,
  2305. presentationTime: 15.872
  2306. }, {
  2307. duration: 1.984,
  2308. map: {
  2309. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  2310. uri: '125000/es/init.m4f'
  2311. },
  2312. resolvedUri: 'https://www.example.com/125000/es/9.m4f',
  2313. timeline: 0,
  2314. uri: '125000/es/9.m4f',
  2315. number: 9,
  2316. presentationTime: 17.856
  2317. }, {
  2318. duration: 1.984,
  2319. map: {
  2320. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  2321. uri: '125000/es/init.m4f'
  2322. },
  2323. resolvedUri: 'https://www.example.com/125000/es/10.m4f',
  2324. timeline: 0,
  2325. uri: '125000/es/10.m4f',
  2326. number: 10,
  2327. presentationTime: 19.84
  2328. }, {
  2329. duration: 1.984,
  2330. map: {
  2331. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  2332. uri: '125000/es/init.m4f'
  2333. },
  2334. resolvedUri: 'https://www.example.com/125000/es/11.m4f',
  2335. timeline: 0,
  2336. uri: '125000/es/11.m4f',
  2337. number: 11,
  2338. presentationTime: 21.824
  2339. }, {
  2340. duration: 1.984,
  2341. map: {
  2342. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  2343. uri: '125000/es/init.m4f'
  2344. },
  2345. resolvedUri: 'https://www.example.com/125000/es/12.m4f',
  2346. timeline: 0,
  2347. uri: '125000/es/12.m4f',
  2348. number: 12,
  2349. presentationTime: 23.808
  2350. }, {
  2351. duration: 1.984,
  2352. map: {
  2353. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  2354. uri: '125000/es/init.m4f'
  2355. },
  2356. resolvedUri: 'https://www.example.com/125000/es/13.m4f',
  2357. timeline: 0,
  2358. uri: '125000/es/13.m4f',
  2359. number: 13,
  2360. presentationTime: 25.792
  2361. }, {
  2362. duration: 1.984,
  2363. map: {
  2364. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  2365. uri: '125000/es/init.m4f'
  2366. },
  2367. resolvedUri: 'https://www.example.com/125000/es/14.m4f',
  2368. timeline: 0,
  2369. uri: '125000/es/14.m4f',
  2370. number: 14,
  2371. presentationTime: 27.776
  2372. }, {
  2373. duration: 0.240000000000002,
  2374. map: {
  2375. resolvedUri: 'https://www.example.com/125000/es/init.m4f',
  2376. uri: '125000/es/init.m4f'
  2377. },
  2378. resolvedUri: 'https://www.example.com/125000/es/15.m4f',
  2379. timeline: 0,
  2380. uri: '125000/es/15.m4f',
  2381. number: 15,
  2382. presentationTime: 29.76
  2383. }]
  2384. }];
  2385. assert.equal(actual.length, 1);
  2386. assert.deepEqual(actual, expected);
  2387. });
  2388. QUnit.test('keySystem info for representation - lowercase UUIDs', function(assert) {
  2389. const NOW = Date.now();
  2390. const widevinePsshB64 = 'AAAANHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABQIARIQCHJ4bvnnRl+jok5bDvj6RQ==';
  2391. const playreadyPsshB64 = 'AAAB5HBzc2gAAAAAmgTweZhAQoarkuZb4IhflQAAAcTEAQAAAQABALoBPABXAFIATQBIAEUAQQBEAEUAUgAgAHgAbQBsAG4AcwA9ACIAaAB0AHQAcAA6AC8ALwBzAGMAaABlAG0AYQBzAC4AbQBpAGMAcgBvAHMAbwBmAHQALgBjAG8AbQAvAEQAUgBNAC8AMgAwADAANwAvADAAMwAvAFAAbABhAHkAUgBlAGEAZAB5AEgAZQBhAGQAZQByACIAIAB2AGUAcgBzAGkAbwBuAD0AIgA0AC4AMAAuADAALgAwACIAPgA8AEQAQQBUAEEAPgA8AFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBFAFkATABFAE4APgAxADYAPAAvAEsARQBZAEwARQBOAD4APABBAEwARwBJAEQAPgBBAEUAUwBDAFQAUgA8AC8AQQBMAEcASQBEAD4APAAvAFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBJAEQAPgBiAG4AaAB5AEMATwBmADUAWAAwAGEAagBvAGsANQBiAEQAdgBqADYAUgBRAD0APQA8AC8ASwBJAEQAPgA8AC8ARABBAFQAQQA+ADwALwBXAFIATQBIAEUAQQBEAEUAUgA+AA==';
  2392. const widevinePsshBytes = decodeB64ToUint8Array(widevinePsshB64);
  2393. const playreadyPsshBytes = decodeB64ToUint8Array(playreadyPsshB64);
  2394. // Content protection info from dash.js demo
  2395. const actual = inheritAttributes(stringToMpdXml(`
  2396. <MPD mediaPresentationDuration="PT30S" xmlns:cenc="urn:mpeg:cenc:2013">
  2397. <BaseURL>https://www.example.com/base/</BaseURL>
  2398. <Period>
  2399. <AdaptationSet mimeType="video/mp4" >
  2400. <ContentProtection schemeIdUri="urn:mpeg:dash:mp4protection:2011" value="cenc" cenc:default_KID="0872786e-f9e7-465f-a3a2-4e5b0ef8fa45" />
  2401. <ContentProtection value="MSPR 2.0" schemeIdUri="urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95">
  2402. <cenc:pssh>AAAB5HBzc2gAAAAAmgTweZhAQoarkuZb4IhflQAAAcTEAQAAAQABALoBPABXAFIATQBIAEUAQQBEAEUAUgAgAHgAbQBsAG4AcwA9ACIAaAB0AHQAcAA6AC8ALwBzAGMAaABlAG0AYQBzAC4AbQBpAGMAcgBvAHMAbwBmAHQALgBjAG8AbQAvAEQAUgBNAC8AMgAwADAANwAvADAAMwAvAFAAbABhAHkAUgBlAGEAZAB5AEgAZQBhAGQAZQByACIAIAB2AGUAcgBzAGkAbwBuAD0AIgA0AC4AMAAuADAALgAwACIAPgA8AEQAQQBUAEEAPgA8AFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBFAFkATABFAE4APgAxADYAPAAvAEsARQBZAEwARQBOAD4APABBAEwARwBJAEQAPgBBAEUAUwBDAFQAUgA8AC8AQQBMAEcASQBEAD4APAAvAFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBJAEQAPgBiAG4AaAB5AEMATwBmADUAWAAwAGEAagBvAGsANQBiAEQAdgBqADYAUgBRAD0APQA8AC8ASwBJAEQAPgA8AC8ARABBAFQAQQA+ADwALwBXAFIATQBIAEUAQQBEAEUAUgA+AA==</cenc:pssh>
  2403. <pro xmlns="urn:microsoft:playready">xAEAAAEAAQC6ATwAVwBSAE0ASABFAEEARABFAFIAIAB4AG0AbABuAHMAPQAiAGgAdAB0AHAAOgAvAC8AcwBjAGgAZQBtAGEAcwAuAG0AaQBjAHIAbwBzAG8AZgB0AC4AYwBvAG0ALwBEAFIATQAvADIAMAAwADcALwAwADMALwBQAGwAYQB5AFIAZQBhAGQAeQBIAGUAYQBkAGUAcgAiACAAdgBlAHIAcwBpAG8AbgA9ACIANAAuADAALgAwAC4AMAAiAD4APABEAEEAVABBAD4APABQAFIATwBUAEUAQwBUAEkATgBGAE8APgA8AEsARQBZAEwARQBOAD4AMQA2ADwALwBLAEUAWQBMAEUATgA+ADwAQQBMAEcASQBEAD4AQQBFAFMAQwBUAFIAPAAvAEEATABHAEkARAA+ADwALwBQAFIATwBUAEUAQwBUAEkATgBGAE8APgA8AEsASQBEAD4AYgBuAGgAeQBDAE8AZgA1AFgAMABhAGoAbwBrADUAYgBEAHYAagA2AFIAUQA9AD0APAAvAEsASQBEAD4APAAvAEQAQQBUAEEAPgA8AC8AVwBSAE0ASABFAEEARABFAFIAPgA=</pro>
  2404. </ContentProtection>
  2405. <ContentProtection value="Widevine" schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
  2406. <cenc:pssh>AAAANHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABQIARIQCHJ4bvnnRl+jok5bDvj6RQ==</cenc:pssh>
  2407. </ContentProtection>
  2408. <Role value="main"></Role>
  2409. <SegmentTemplate></SegmentTemplate>
  2410. <Representation
  2411. bandwidth="5000000"
  2412. codecs="avc1.64001e"
  2413. height="404"
  2414. id="test"
  2415. width="720">
  2416. </Representation>
  2417. </AdaptationSet>
  2418. </Period>
  2419. </MPD>
  2420. `), { NOW });
  2421. // inconsistent quoting because of quote-props
  2422. const expected = {
  2423. contentSteeringInfo: null,
  2424. eventStream: [],
  2425. locations: undefined,
  2426. representationInfo: [{
  2427. attributes: {
  2428. 'bandwidth': 5000000,
  2429. 'baseUrl': 'https://www.example.com/base/',
  2430. 'codecs': 'avc1.64001e',
  2431. 'contentProtection': {
  2432. 'com.microsoft.playready': {
  2433. attributes: {
  2434. schemeIdUri: 'urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95',
  2435. value: 'MSPR 2.0'
  2436. },
  2437. pssh: playreadyPsshBytes
  2438. },
  2439. 'com.widevine.alpha': {
  2440. attributes: {
  2441. schemeIdUri: 'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed',
  2442. value: 'Widevine'
  2443. },
  2444. pssh: widevinePsshBytes
  2445. },
  2446. 'mp4protection': {
  2447. attributes: {
  2448. 'cenc:default_KID': '0872786e-f9e7-465f-a3a2-4e5b0ef8fa45',
  2449. 'schemeIdUri': 'urn:mpeg:dash:mp4protection:2011',
  2450. 'value': 'cenc'
  2451. }
  2452. }
  2453. },
  2454. 'height': 404,
  2455. 'id': 'test',
  2456. 'mediaPresentationDuration': 30,
  2457. 'mimeType': 'video/mp4',
  2458. 'periodStart': 0,
  2459. 'role': {
  2460. value: 'main'
  2461. },
  2462. 'sourceDuration': 30,
  2463. 'type': 'static',
  2464. 'width': 720,
  2465. NOW,
  2466. 'clientOffset': 0,
  2467. 'xmlns:cenc': 'urn:mpeg:cenc:2013'
  2468. },
  2469. segmentInfo: {
  2470. template: {}
  2471. }
  2472. }]
  2473. };
  2474. assert.equal(actual.representationInfo.length, 1);
  2475. assert.deepEqual(actual, expected);
  2476. });
  2477. QUnit.test('keySystem info for representation - uppercase UUIDs', function(assert) {
  2478. const NOW = Date.now();
  2479. const widevinePsshB64 = 'AAAANHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABQIARIQCHJ4bvnnRl+jok5bDvj6RQ==';
  2480. const playreadyPsshB64 = 'AAAB5HBzc2gAAAAAmgTweZhAQoarkuZb4IhflQAAAcTEAQAAAQABALoBPABXAFIATQBIAEUAQQBEAEUAUgAgAHgAbQBsAG4AcwA9ACIAaAB0AHQAcAA6AC8ALwBzAGMAaABlAG0AYQBzAC4AbQBpAGMAcgBvAHMAbwBmAHQALgBjAG8AbQAvAEQAUgBNAC8AMgAwADAANwAvADAAMwAvAFAAbABhAHkAUgBlAGEAZAB5AEgAZQBhAGQAZQByACIAIAB2AGUAcgBzAGkAbwBuAD0AIgA0AC4AMAAuADAALgAwACIAPgA8AEQAQQBUAEEAPgA8AFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBFAFkATABFAE4APgAxADYAPAAvAEsARQBZAEwARQBOAD4APABBAEwARwBJAEQAPgBBAEUAUwBDAFQAUgA8AC8AQQBMAEcASQBEAD4APAAvAFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBJAEQAPgBiAG4AaAB5AEMATwBmADUAWAAwAGEAagBvAGsANQBiAEQAdgBqADYAUgBRAD0APQA8AC8ASwBJAEQAPgA8AC8ARABBAFQAQQA+ADwALwBXAFIATQBIAEUAQQBEAEUAUgA+AA==';
  2481. const widevinePsshBytes = decodeB64ToUint8Array(widevinePsshB64);
  2482. const playreadyPsshBytes = decodeB64ToUint8Array(playreadyPsshB64);
  2483. // Content protection info from dash.js demo
  2484. const actual = inheritAttributes(stringToMpdXml(`
  2485. <MPD mediaPresentationDuration="PT30S" xmlns:cenc="urn:mpeg:cenc:2013">
  2486. <BaseURL>https://www.example.com/base/</BaseURL>
  2487. <Period>
  2488. <AdaptationSet mimeType="video/mp4" >
  2489. <ContentProtection schemeIdUri="urn:mpeg:dash:mp4protection:2011" value="cenc" cenc:default_KID="0872786E-F9E7-465F-A3A2-4E5B0EF8FA45" />
  2490. <ContentProtection value="MSPR 2.0" schemeIdUri="urn:uuid:9A04F079-9840-4286-AB92-E65BE0885F95">
  2491. <cenc:pssh>AAAB5HBzc2gAAAAAmgTweZhAQoarkuZb4IhflQAAAcTEAQAAAQABALoBPABXAFIATQBIAEUAQQBEAEUAUgAgAHgAbQBsAG4AcwA9ACIAaAB0AHQAcAA6AC8ALwBzAGMAaABlAG0AYQBzAC4AbQBpAGMAcgBvAHMAbwBmAHQALgBjAG8AbQAvAEQAUgBNAC8AMgAwADAANwAvADAAMwAvAFAAbABhAHkAUgBlAGEAZAB5AEgAZQBhAGQAZQByACIAIAB2AGUAcgBzAGkAbwBuAD0AIgA0AC4AMAAuADAALgAwACIAPgA8AEQAQQBUAEEAPgA8AFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBFAFkATABFAE4APgAxADYAPAAvAEsARQBZAEwARQBOAD4APABBAEwARwBJAEQAPgBBAEUAUwBDAFQAUgA8AC8AQQBMAEcASQBEAD4APAAvAFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBJAEQAPgBiAG4AaAB5AEMATwBmADUAWAAwAGEAagBvAGsANQBiAEQAdgBqADYAUgBRAD0APQA8AC8ASwBJAEQAPgA8AC8ARABBAFQAQQA+ADwALwBXAFIATQBIAEUAQQBEAEUAUgA+AA==</cenc:pssh>
  2492. <pro xmlns="urn:microsoft:playready">xAEAAAEAAQC6ATwAVwBSAE0ASABFAEEARABFAFIAIAB4AG0AbABuAHMAPQAiAGgAdAB0AHAAOgAvAC8AcwBjAGgAZQBtAGEAcwAuAG0AaQBjAHIAbwBzAG8AZgB0AC4AYwBvAG0ALwBEAFIATQAvADIAMAAwADcALwAwADMALwBQAGwAYQB5AFIAZQBhAGQAeQBIAGUAYQBkAGUAcgAiACAAdgBlAHIAcwBpAG8AbgA9ACIANAAuADAALgAwAC4AMAAiAD4APABEAEEAVABBAD4APABQAFIATwBUAEUAQwBUAEkATgBGAE8APgA8AEsARQBZAEwARQBOAD4AMQA2ADwALwBLAEUAWQBMAEUATgA+ADwAQQBMAEcASQBEAD4AQQBFAFMAQwBUAFIAPAAvAEEATABHAEkARAA+ADwALwBQAFIATwBUAEUAQwBUAEkATgBGAE8APgA8AEsASQBEAD4AYgBuAGgAeQBDAE8AZgA1AFgAMABhAGoAbwBrADUAYgBEAHYAagA2AFIAUQA9AD0APAAvAEsASQBEAD4APAAvAEQAQQBUAEEAPgA8AC8AVwBSAE0ASABFAEEARABFAFIAPgA=</pro>
  2493. </ContentProtection>
  2494. <ContentProtection value="Widevine" schemeIdUri="urn:uuid:EDEF8BA9-79D6-4ACE-A3C8-27DCD51D21ED">
  2495. <cenc:pssh>AAAANHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABQIARIQCHJ4bvnnRl+jok5bDvj6RQ==</cenc:pssh>
  2496. </ContentProtection>
  2497. <Role value="main"></Role>
  2498. <SegmentTemplate></SegmentTemplate>
  2499. <Representation
  2500. bandwidth="5000000"
  2501. codecs="avc1.64001e"
  2502. height="404"
  2503. id="test"
  2504. width="720">
  2505. </Representation>
  2506. </AdaptationSet>
  2507. </Period>
  2508. </MPD>
  2509. `), { NOW });
  2510. // inconsistent quoting because of quote-props
  2511. const expected = {
  2512. contentSteeringInfo: null,
  2513. eventStream: [],
  2514. locations: undefined,
  2515. representationInfo: [{
  2516. attributes: {
  2517. 'bandwidth': 5000000,
  2518. 'baseUrl': 'https://www.example.com/base/',
  2519. 'codecs': 'avc1.64001e',
  2520. 'contentProtection': {
  2521. 'com.microsoft.playready': {
  2522. attributes: {
  2523. schemeIdUri: 'urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95',
  2524. value: 'MSPR 2.0'
  2525. },
  2526. pssh: playreadyPsshBytes
  2527. },
  2528. 'com.widevine.alpha': {
  2529. attributes: {
  2530. schemeIdUri: 'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed',
  2531. value: 'Widevine'
  2532. },
  2533. pssh: widevinePsshBytes
  2534. },
  2535. 'mp4protection': {
  2536. attributes: {
  2537. 'cenc:default_KID': '0872786E-F9E7-465F-A3A2-4E5B0EF8FA45',
  2538. 'schemeIdUri': 'urn:mpeg:dash:mp4protection:2011',
  2539. 'value': 'cenc'
  2540. }
  2541. }
  2542. },
  2543. 'height': 404,
  2544. 'id': 'test',
  2545. 'mediaPresentationDuration': 30,
  2546. 'mimeType': 'video/mp4',
  2547. 'periodStart': 0,
  2548. 'role': {
  2549. value: 'main'
  2550. },
  2551. 'sourceDuration': 30,
  2552. 'type': 'static',
  2553. 'width': 720,
  2554. NOW,
  2555. 'clientOffset': 0,
  2556. 'xmlns:cenc': 'urn:mpeg:cenc:2013'
  2557. },
  2558. segmentInfo: {
  2559. template: {}
  2560. }
  2561. }]
  2562. };
  2563. assert.equal(actual.representationInfo.length, 1);
  2564. assert.deepEqual(actual, expected);
  2565. });
  2566. QUnit.test('gets EventStream data from toEventStream', function(assert) {
  2567. const mpd = stringToMpdXml(`
  2568. <MPD mediaPresentationDuration="PT30S" xmlns:cenc="urn:mpeg:cenc:2013">
  2569. <Period id="dai_pod-0001065804-ad-1" start="PT17738H17M14.156S" duration="PT9.977S">
  2570. <BaseURL>https://www.example.com/base/</BaseURL>
  2571. <SegmentTemplate media="$RepresentationID$/$Number$.mp4" initialization="$RepresentationID$/init.mp4"/>
  2572. <EventStream schemeIdUri="urn:google:dai:2018" timescale="1000" contentEncoding="foo" presentationTimeOffset="1">
  2573. <Event presentationTime="100" duration="0" id="0" messageData="foo"/>
  2574. <Event presentationTime="900" duration="0" id="5" messageData="bar"/>
  2575. <Event presentationTime="1900" duration="0" id="6" messageData="foo_bar"/>
  2576. </EventStream>
  2577. </Period>
  2578. </MPD>`);
  2579. const expected = [
  2580. {
  2581. end: 2.1,
  2582. id: '0',
  2583. messageData: 'foo',
  2584. schemeIdUri: 'urn:google:dai:2018',
  2585. start: 2.1,
  2586. value: undefined,
  2587. contentEncoding: 'foo',
  2588. presentationTimeOffset: 1
  2589. },
  2590. {
  2591. end: 2.9,
  2592. id: '5',
  2593. messageData: 'bar',
  2594. schemeIdUri: 'urn:google:dai:2018',
  2595. start: 2.9,
  2596. value: undefined,
  2597. contentEncoding: 'foo',
  2598. presentationTimeOffset: 1
  2599. },
  2600. {
  2601. end: 3.9,
  2602. id: '6',
  2603. messageData: 'foo_bar',
  2604. schemeIdUri: 'urn:google:dai:2018',
  2605. start: 3.9,
  2606. value: undefined,
  2607. contentEncoding: 'foo',
  2608. presentationTimeOffset: 1
  2609. }
  2610. ];
  2611. const firstPeriod = { node: findChildren(mpd, 'Period')[0], attributes: { start: 2 } };
  2612. const eventStreams = toEventStream(firstPeriod);
  2613. assert.deepEqual(eventStreams, expected, 'toEventStream returns the expected object');
  2614. });
  2615. QUnit.test('can get EventStream data from toEventStream with no schemeIdUri', function(assert) {
  2616. const mpd = stringToMpdXml(`
  2617. <MPD mediaPresentationDuration="PT30S" xmlns:cenc="urn:mpeg:cenc:2013">
  2618. <Period id="dai_pod-0001065804-ad-1" start="PT17738H17M14.156S" duration="PT9.977S">
  2619. <BaseURL>https://www.example.com/base/</BaseURL>
  2620. <SegmentTemplate media="$RepresentationID$/$Number$.mp4" initialization="$RepresentationID$/init.mp4"/>
  2621. <EventStream timescale="1000">
  2622. <Event presentationTime="100" duration="0" id="0" messageData="foo"/>
  2623. <Event presentationTime="900" duration="0" id="5" messageData="bar"/>
  2624. <Event presentationTime="1900" duration="0" id="6" messageData="foo_bar"/>
  2625. </EventStream>
  2626. </Period>
  2627. </MPD>`);
  2628. const expected = [
  2629. {
  2630. end: 2.1,
  2631. id: '0',
  2632. messageData: 'foo',
  2633. schemeIdUri: undefined,
  2634. start: 2.1,
  2635. value: undefined,
  2636. contentEncoding: undefined,
  2637. presentationTimeOffset: 0
  2638. },
  2639. {
  2640. end: 2.9,
  2641. id: '5',
  2642. messageData: 'bar',
  2643. schemeIdUri: undefined,
  2644. start: 2.9,
  2645. value: undefined,
  2646. contentEncoding: undefined,
  2647. presentationTimeOffset: 0
  2648. },
  2649. {
  2650. end: 3.9,
  2651. id: '6',
  2652. messageData: 'foo_bar',
  2653. schemeIdUri: undefined,
  2654. start: 3.9,
  2655. value: undefined,
  2656. contentEncoding: undefined,
  2657. presentationTimeOffset: 0
  2658. }
  2659. ];
  2660. const firstPeriod = { node: findChildren(mpd, 'Period')[0], attributes: { start: 2} };
  2661. const eventStreams = toEventStream(firstPeriod);
  2662. assert.deepEqual(eventStreams, expected, 'toEventStream returns the expected object');
  2663. });
  2664. QUnit.test('gets eventStream from inheritAttributes', function(assert) {
  2665. const mpd = stringToMpdXml(`
  2666. <MPD mediaPresentationDuration="PT30S" xmlns:cenc="urn:mpeg:cenc:2013">
  2667. <Period id="dai_pod-0001065804-ad-1" start="PT0H0M14.9S" duration="PT9.977S">
  2668. <BaseURL>https://www.example.com/base/</BaseURL>
  2669. <SegmentTemplate media="$RepresentationID$/$Number$.mp4" initialization="$RepresentationID$/init.mp4"/>
  2670. <EventStream schemeIdUri="urn:google:dai:2018" timescale="1000" value="foo">
  2671. <Event presentationTime="100" duration="0" id="0" messageData="foo"/>
  2672. <Event presentationTime="1100" duration="0" id="5" messageData="bar"/>
  2673. <Event presentationTime="2100" duration="0" id="6" messageData="foo_bar"/>
  2674. </EventStream>
  2675. </Period>
  2676. </MPD>`);
  2677. const expected = {
  2678. contentSteeringInfo: null,
  2679. eventStream: [
  2680. {
  2681. end: 15,
  2682. id: '0',
  2683. messageData: 'foo',
  2684. schemeIdUri: 'urn:google:dai:2018',
  2685. start: 15,
  2686. value: 'foo',
  2687. contentEncoding: undefined,
  2688. presentationTimeOffset: 0
  2689. },
  2690. {
  2691. end: 16,
  2692. id: '5',
  2693. messageData: 'bar',
  2694. schemeIdUri: 'urn:google:dai:2018',
  2695. start: 16,
  2696. value: 'foo',
  2697. contentEncoding: undefined,
  2698. presentationTimeOffset: 0
  2699. },
  2700. {
  2701. end: 17,
  2702. id: '6',
  2703. messageData: 'foo_bar',
  2704. schemeIdUri: 'urn:google:dai:2018',
  2705. start: 17,
  2706. value: 'foo',
  2707. contentEncoding: undefined,
  2708. presentationTimeOffset: 0
  2709. }
  2710. ],
  2711. locations: undefined,
  2712. representationInfo: []
  2713. };
  2714. const eventStreams = inheritAttributes(mpd);
  2715. assert.deepEqual(eventStreams, expected, 'inheritAttributes returns the expected object');
  2716. });
  2717. QUnit.test('can get EventStream data from toEventStream with data in Event tags', function(assert) {
  2718. const mpd = stringToMpdXml(`
  2719. <MPD mediaPresentationDuration="PT30S" xmlns:cenc="urn:mpeg:cenc:2013">
  2720. <Period id="dai_pod-0001065804-ad-1" start="PT17738H17M14.156S" duration="PT9.977S">
  2721. <BaseURL>https://www.example.com/base/</BaseURL>
  2722. <SegmentTemplate media="$RepresentationID$/$Number$.mp4" initialization="$RepresentationID$/init.mp4"/>
  2723. <EventStream timescale="1000">
  2724. <Event presentationTime="100" duration="0" id="0">foo</Event>
  2725. <Event presentationTime="900" duration="0" id="5">bar</Event>
  2726. <Event presentationTime="1900" duration="0" id="6">foo_bar</Event>
  2727. </EventStream>
  2728. </Period>
  2729. </MPD>`);
  2730. const expected = [
  2731. {
  2732. end: 2.1,
  2733. id: '0',
  2734. messageData: 'foo',
  2735. schemeIdUri: undefined,
  2736. start: 2.1,
  2737. value: undefined,
  2738. contentEncoding: undefined,
  2739. presentationTimeOffset: 0
  2740. },
  2741. {
  2742. end: 2.9,
  2743. id: '5',
  2744. messageData: 'bar',
  2745. schemeIdUri: undefined,
  2746. start: 2.9,
  2747. value: undefined,
  2748. contentEncoding: undefined,
  2749. presentationTimeOffset: 0
  2750. },
  2751. {
  2752. end: 3.9,
  2753. id: '6',
  2754. messageData: 'foo_bar',
  2755. schemeIdUri: undefined,
  2756. start: 3.9,
  2757. value: undefined,
  2758. contentEncoding: undefined,
  2759. presentationTimeOffset: 0
  2760. }
  2761. ];
  2762. const firstPeriod = { node: findChildren(mpd, 'Period')[0], attributes: { start: 2} };
  2763. const eventStreams = toEventStream(firstPeriod);
  2764. assert.deepEqual(eventStreams, expected, 'toEventStream returns the expected object');
  2765. });
  2766. QUnit.test('gets eventStream from inheritAttributes with data in Event tags', function(assert) {
  2767. const mpd = stringToMpdXml(`
  2768. <MPD mediaPresentationDuration="PT30S" xmlns:cenc="urn:mpeg:cenc:2013">
  2769. <Period id="dai_pod-0001065804-ad-1" start="PT0H0M14.9S" duration="PT9.977S">
  2770. <BaseURL>https://www.example.com/base/</BaseURL>
  2771. <SegmentTemplate media="$RepresentationID$/$Number$.mp4" initialization="$RepresentationID$/init.mp4"/>
  2772. <EventStream schemeIdUri="urn:google:dai:2018" timescale="1000" value="foo">
  2773. <Event presentationTime="100" duration="0" id="0">foo</Event>
  2774. <Event presentationTime="1100" duration="0" id="5">bar</Event>
  2775. <Event presentationTime="2100" duration="0" id="6">foo_bar</Event>
  2776. </EventStream>
  2777. </Period>
  2778. </MPD>`);
  2779. const expected = {
  2780. contentSteeringInfo: null,
  2781. eventStream: [
  2782. {
  2783. end: 15,
  2784. id: '0',
  2785. messageData: 'foo',
  2786. schemeIdUri: 'urn:google:dai:2018',
  2787. start: 15,
  2788. value: 'foo',
  2789. contentEncoding: undefined,
  2790. presentationTimeOffset: 0
  2791. },
  2792. {
  2793. end: 16,
  2794. id: '5',
  2795. messageData: 'bar',
  2796. schemeIdUri: 'urn:google:dai:2018',
  2797. start: 16,
  2798. value: 'foo',
  2799. contentEncoding: undefined,
  2800. presentationTimeOffset: 0
  2801. },
  2802. {
  2803. end: 17,
  2804. id: '6',
  2805. messageData: 'foo_bar',
  2806. schemeIdUri: 'urn:google:dai:2018',
  2807. start: 17,
  2808. value: 'foo',
  2809. contentEncoding: undefined,
  2810. presentationTimeOffset: 0
  2811. }
  2812. ],
  2813. locations: undefined,
  2814. representationInfo: []
  2815. };
  2816. const eventStreams = inheritAttributes(mpd);
  2817. assert.deepEqual(eventStreams, expected, 'inheritAttributes returns the expected object');
  2818. });