segmentTemplate.test.js 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600
  1. import QUnit from 'qunit';
  2. import {
  3. constructTemplateUrl,
  4. parseTemplateInfo,
  5. segmentsFromTemplate
  6. } from '../../src/segment/segmentTemplate';
  7. QUnit.module('segmentTemplate - constructTemplateUrl');
  8. QUnit.test('does not change url with no identifiers', function(assert) {
  9. const url = 'path/to/segment.mp4';
  10. assert.equal(constructTemplateUrl(url, {}), url, 'no change');
  11. });
  12. QUnit.test('replaces each identifier individually', function(assert) {
  13. const values = {
  14. RepresentationID: 'Rep1',
  15. Bandwidth: 1000,
  16. Number: 2,
  17. Time: 2000
  18. };
  19. const cases = [
  20. {
  21. url: '/$RepresentationID$/segment.mp4',
  22. expected: '/Rep1/segment.mp4'
  23. },
  24. {
  25. url: '/$Bandwidth$/segment.mp4',
  26. expected: '/1000/segment.mp4'
  27. },
  28. {
  29. url: '/$Number$/segment.mp4',
  30. expected: '/2/segment.mp4'
  31. },
  32. {
  33. url: '/$Time$/segment.mp4',
  34. expected: '/2000/segment.mp4'
  35. },
  36. {
  37. url: '/$$/segment.mp4',
  38. expected: '/$/segment.mp4'
  39. }
  40. ];
  41. cases.forEach(test => {
  42. assert.equal(constructTemplateUrl(test.url, values), test.expected, `constructs ${test.url}`);
  43. });
  44. });
  45. QUnit.test('replaces multiple identifiers in url', function(assert) {
  46. assert.equal(
  47. constructTemplateUrl(
  48. '$$$Time$$$$$/$RepresentationID$/$Bandwidth$/$Number$-$Time$-segment-$Number$.mp4',
  49. {
  50. RepresentationID: 'Rep1',
  51. Bandwidth: 1000,
  52. Number: 2,
  53. Time: 2000
  54. }
  55. ),
  56. '$2000$$/Rep1/1000/2-2000-segment-2.mp4',
  57. 'correctly replaces multiple identifiers in single url'
  58. );
  59. });
  60. QUnit.test('does not replace unknown identifiers', function(assert) {
  61. assert.equal(
  62. constructTemplateUrl(
  63. '/$UNKNOWN$/$RepresentationID$/$UNKOWN2$/$Number$.mp4',
  64. {
  65. RepresentationID: 'Rep1',
  66. Number: 1
  67. }
  68. ),
  69. '/$UNKNOWN$/Rep1/$UNKOWN2$/1.mp4',
  70. 'ignores unknown identifiers'
  71. );
  72. });
  73. QUnit.test('honors padding format tag', function(assert) {
  74. assert.equal(
  75. constructTemplateUrl(
  76. '/$Number%03d$/segment.mp4',
  77. { Number: 7 }
  78. ),
  79. '/007/segment.mp4',
  80. 'correctly adds padding when format tag present'
  81. );
  82. });
  83. QUnit.test('does not add padding when value is longer than width', function(assert) {
  84. assert.equal(
  85. constructTemplateUrl(
  86. '/$Bandwidth%06d$/segment.mp4',
  87. { Bandwidth: 999999999 }
  88. ),
  89. '/999999999/segment.mp4',
  90. 'no padding when value longer than format width'
  91. );
  92. });
  93. QUnit.test('does not use padding format tag for $RepresentationID$', function(assert) {
  94. assert.equal(
  95. constructTemplateUrl(
  96. '/$RepresentationID%09d$/$Number%03d$/segment.mp4',
  97. { RepresentationID: 'Rep1', Number: 7 }
  98. ),
  99. '/Rep1/007/segment.mp4',
  100. 'ignores format tag for $RepresentationID$'
  101. );
  102. });
  103. QUnit.module('segmentTemplate - parseTemplateInfo');
  104. QUnit.test(
  105. 'one media segment when no @duration attribute or SegmentTimeline element',
  106. function(assert) {
  107. const attributes = {
  108. startNumber: 3,
  109. timescale: 1000,
  110. sourceDuration: 42,
  111. periodIndex: 1,
  112. type: 'static',
  113. periodStart: 0
  114. };
  115. assert.deepEqual(
  116. parseTemplateInfo(attributes, void 0),
  117. [ { number: 3, duration: 42, time: 0, timeline: 0 }],
  118. 'creates segment list of one media segment when no @duration attribute or timeline'
  119. );
  120. }
  121. );
  122. QUnit.test('uses @duration attribute when present', function(assert) {
  123. const attributes = {
  124. startNumber: 0,
  125. timescale: 1000,
  126. sourceDuration: 16,
  127. duration: 6000,
  128. periodIndex: 1,
  129. type: 'static',
  130. periodStart: 0
  131. };
  132. assert.deepEqual(
  133. parseTemplateInfo(attributes, []),
  134. [
  135. {
  136. number: 0,
  137. duration: 6,
  138. timeline: 0,
  139. time: 0
  140. },
  141. {
  142. number: 1,
  143. duration: 6,
  144. timeline: 0,
  145. time: 6000
  146. },
  147. {
  148. number: 2,
  149. duration: 4,
  150. timeline: 0,
  151. time: 12000
  152. }
  153. ],
  154. 'correctly parses segment durations and start times with @duration attribute'
  155. );
  156. });
  157. QUnit.test('parseByDuration allows non zero startNumber', function(assert) {
  158. const attributes = {
  159. startNumber: 101,
  160. timescale: 1000,
  161. sourceDuration: 16,
  162. duration: 6000,
  163. periodIndex: 1,
  164. type: 'static',
  165. periodStart: 0
  166. };
  167. assert.deepEqual(
  168. parseTemplateInfo(attributes, []),
  169. [
  170. {
  171. number: 101,
  172. duration: 6,
  173. timeline: 0,
  174. time: 0
  175. },
  176. {
  177. number: 102,
  178. duration: 6,
  179. timeline: 0,
  180. time: 6000
  181. },
  182. {
  183. number: 103,
  184. duration: 4,
  185. timeline: 0,
  186. time: 12000
  187. }
  188. ],
  189. 'allows non zero startNumber'
  190. );
  191. });
  192. QUnit.test('parseByDuration defaults 1 for startNumber and timescale', function(assert) {
  193. const attributes = {
  194. sourceDuration: 11,
  195. duration: '4',
  196. periodIndex: 1,
  197. type: 'static',
  198. periodStart: 0
  199. };
  200. assert.deepEqual(
  201. parseTemplateInfo(attributes, []),
  202. [
  203. {
  204. number: 1,
  205. duration: 4,
  206. timeline: 0,
  207. time: 0
  208. },
  209. {
  210. number: 2,
  211. duration: 4,
  212. timeline: 0,
  213. time: 4
  214. },
  215. {
  216. number: 3,
  217. duration: 3,
  218. timeline: 0,
  219. time: 8
  220. }
  221. ],
  222. 'uses default startNumber and timescale value of 1'
  223. );
  224. });
  225. QUnit.test('parseByDuration uses endNumber and has correct duration', function(assert) {
  226. const attributes = {
  227. sourceDuration: 11,
  228. duration: '4',
  229. periodIndex: 1,
  230. endNumber: '2',
  231. type: 'static',
  232. periodStart: 0
  233. };
  234. assert.deepEqual(
  235. parseTemplateInfo(attributes, []),
  236. [
  237. {
  238. number: 1,
  239. duration: 4,
  240. timeline: 0,
  241. time: 0
  242. },
  243. {
  244. number: 2,
  245. duration: 7,
  246. timeline: 0,
  247. time: 4
  248. }
  249. ],
  250. 'uses default startNumber and timescale value of 1'
  251. );
  252. });
  253. QUnit.test('uses SegmentTimeline info when no @duration attribute', function(assert) {
  254. const attributes = {
  255. startNumber: 0,
  256. sourceDuration: 16,
  257. timescale: 1000,
  258. periodIndex: 1,
  259. type: 'static',
  260. periodStart: 0
  261. };
  262. const segmentTimeline = [
  263. {
  264. t: 0,
  265. d: 6000
  266. },
  267. {
  268. d: 2000
  269. },
  270. {
  271. d: 3000
  272. },
  273. {
  274. d: 5000
  275. }
  276. ];
  277. assert.deepEqual(
  278. parseTemplateInfo(attributes, segmentTimeline),
  279. [
  280. {
  281. number: 0,
  282. duration: 6,
  283. time: 0,
  284. timeline: 0
  285. },
  286. {
  287. number: 1,
  288. duration: 2,
  289. time: 6000,
  290. timeline: 0
  291. },
  292. {
  293. number: 2,
  294. duration: 3,
  295. time: 8000,
  296. timeline: 0
  297. },
  298. {
  299. number: 3,
  300. duration: 5,
  301. time: 11000,
  302. timeline: 0
  303. }
  304. ],
  305. 'correctly calculates segment durations and start times with SegmentTimeline'
  306. );
  307. });
  308. QUnit.test('parseByTimeline allows non zero startNumber', function(assert) {
  309. const attributes = {
  310. startNumber: 101,
  311. sourceDuration: 16,
  312. timescale: 1000,
  313. periodIndex: 1,
  314. type: 'static',
  315. periodStart: 0
  316. };
  317. const segmentTimeline = [
  318. {
  319. t: 0,
  320. d: 6000
  321. },
  322. {
  323. d: 2000
  324. },
  325. {
  326. d: 3000
  327. },
  328. {
  329. d: 5000
  330. }
  331. ];
  332. assert.deepEqual(
  333. parseTemplateInfo(attributes, segmentTimeline),
  334. [
  335. {
  336. number: 101,
  337. duration: 6,
  338. time: 0,
  339. timeline: 0
  340. },
  341. {
  342. number: 102,
  343. duration: 2,
  344. time: 6000,
  345. timeline: 0
  346. },
  347. {
  348. number: 103,
  349. duration: 3,
  350. time: 8000,
  351. timeline: 0
  352. },
  353. {
  354. number: 104,
  355. duration: 5,
  356. time: 11000,
  357. timeline: 0
  358. }
  359. ],
  360. 'allows non zero startNumber'
  361. );
  362. });
  363. QUnit.test('parseByTimeline defaults 1 for startNumber and timescale', function(assert) {
  364. const attributes = {
  365. sourceDuration: 11,
  366. periodIndex: 1,
  367. type: 'static',
  368. periodStart: 0
  369. };
  370. const segmentTimeline = [
  371. {
  372. t: 0,
  373. d: 4
  374. },
  375. {
  376. d: 2
  377. },
  378. {
  379. d: 3
  380. },
  381. {
  382. d: 2
  383. }
  384. ];
  385. assert.deepEqual(
  386. parseTemplateInfo(attributes, segmentTimeline),
  387. [
  388. {
  389. number: 1,
  390. duration: 4,
  391. time: 0,
  392. timeline: 0
  393. },
  394. {
  395. number: 2,
  396. duration: 2,
  397. time: 4,
  398. timeline: 0
  399. },
  400. {
  401. number: 3,
  402. duration: 3,
  403. time: 6,
  404. timeline: 0
  405. },
  406. {
  407. number: 4,
  408. duration: 2,
  409. time: 9,
  410. timeline: 0
  411. }
  412. ],
  413. 'defaults to 1 for startNumber and timescale'
  414. );
  415. });
  416. QUnit.test('defaults SegmentTimeline.S@t to 0 for first segment', function(assert) {
  417. const attributes = {
  418. startNumber: 0,
  419. sourceDuration: 16,
  420. timescale: 1000,
  421. periodIndex: 1,
  422. type: 'static',
  423. periodStart: 0
  424. };
  425. const segmentTimeline = [
  426. {
  427. d: 6000
  428. },
  429. {
  430. d: 2000
  431. },
  432. {
  433. d: 3000
  434. },
  435. {
  436. d: 5000
  437. }
  438. ];
  439. assert.deepEqual(
  440. parseTemplateInfo(attributes, segmentTimeline),
  441. [
  442. {
  443. number: 0,
  444. duration: 6,
  445. time: 0,
  446. timeline: 0
  447. },
  448. {
  449. number: 1,
  450. duration: 2,
  451. time: 6000,
  452. timeline: 0
  453. },
  454. {
  455. number: 2,
  456. duration: 3,
  457. time: 8000,
  458. timeline: 0
  459. },
  460. {
  461. number: 3,
  462. duration: 5,
  463. time: 11000,
  464. timeline: 0
  465. }
  466. ],
  467. 'uses default value of 0'
  468. );
  469. });
  470. QUnit.test('allows non zero starting SegmentTimeline.S@t value', function(assert) {
  471. const attributes = {
  472. startNumber: 0,
  473. sourceDuration: 16,
  474. timescale: 1000,
  475. periodIndex: 1,
  476. type: 'static',
  477. periodStart: 0
  478. };
  479. const segmentTimeline = [
  480. {
  481. t: 42000,
  482. d: 6000
  483. },
  484. {
  485. d: 2000
  486. },
  487. {
  488. d: 3000
  489. },
  490. {
  491. d: 5000
  492. }
  493. ];
  494. assert.deepEqual(
  495. parseTemplateInfo(attributes, segmentTimeline),
  496. [
  497. {
  498. number: 0,
  499. duration: 6,
  500. time: 42000,
  501. timeline: 0
  502. },
  503. {
  504. number: 1,
  505. duration: 2,
  506. time: 48000,
  507. timeline: 0
  508. },
  509. {
  510. number: 2,
  511. duration: 3,
  512. time: 50000,
  513. timeline: 0
  514. },
  515. {
  516. number: 3,
  517. duration: 5,
  518. time: 53000,
  519. timeline: 0
  520. }
  521. ],
  522. 'allows non zero SegmentTimeline.S@t start value'
  523. );
  524. });
  525. QUnit.test('honors @r repeat attribute for SegmentTimeline.S', function(assert) {
  526. const attributes = {
  527. startNumber: 0,
  528. sourceDuration: 16,
  529. timescale: 1000,
  530. periodIndex: 1,
  531. type: 'static',
  532. periodStart: 0
  533. };
  534. const segmentTimeline = [
  535. {
  536. t: 0,
  537. d: 6000
  538. },
  539. {
  540. d: 1000,
  541. r: 3
  542. },
  543. {
  544. d: 5000
  545. },
  546. {
  547. d: 1000
  548. }
  549. ];
  550. assert.deepEqual(
  551. parseTemplateInfo(attributes, segmentTimeline),
  552. [
  553. {
  554. number: 0,
  555. duration: 6,
  556. time: 0,
  557. timeline: 0
  558. },
  559. {
  560. number: 1,
  561. duration: 1,
  562. time: 6000,
  563. timeline: 0
  564. },
  565. {
  566. number: 2,
  567. duration: 1,
  568. time: 7000,
  569. timeline: 0
  570. },
  571. {
  572. number: 3,
  573. duration: 1,
  574. time: 8000,
  575. timeline: 0
  576. },
  577. {
  578. number: 4,
  579. duration: 1,
  580. time: 9000,
  581. timeline: 0
  582. },
  583. {
  584. number: 5,
  585. duration: 5,
  586. time: 10000,
  587. timeline: 0
  588. },
  589. {
  590. number: 6,
  591. duration: 1,
  592. time: 15000,
  593. timeline: 0
  594. }
  595. ],
  596. 'correctly uses @r repeat attribute'
  597. );
  598. });
  599. QUnit.test('correctly handles negative @r repeat value', function(assert) {
  600. const attributes = {
  601. startNumber: 0,
  602. sourceDuration: 16,
  603. timescale: 1000,
  604. periodIndex: 1,
  605. type: 'static',
  606. periodStart: 0
  607. };
  608. const segmentTimeline = [
  609. {
  610. t: 0,
  611. d: 6000
  612. },
  613. {
  614. d: 1000,
  615. r: -1
  616. },
  617. {
  618. t: 10000,
  619. d: 5000
  620. },
  621. {
  622. d: 1000
  623. }
  624. ];
  625. assert.deepEqual(
  626. parseTemplateInfo(attributes, segmentTimeline),
  627. [
  628. {
  629. number: 0,
  630. duration: 6,
  631. time: 0,
  632. timeline: 0
  633. },
  634. {
  635. number: 1,
  636. duration: 1,
  637. time: 6000,
  638. timeline: 0
  639. },
  640. {
  641. number: 2,
  642. duration: 1,
  643. time: 7000,
  644. timeline: 0
  645. },
  646. {
  647. number: 3,
  648. duration: 1,
  649. time: 8000,
  650. timeline: 0
  651. },
  652. {
  653. number: 4,
  654. duration: 1,
  655. time: 9000,
  656. timeline: 0
  657. },
  658. {
  659. number: 5,
  660. duration: 5,
  661. time: 10000,
  662. timeline: 0
  663. },
  664. {
  665. number: 6,
  666. duration: 1,
  667. time: 15000,
  668. timeline: 0
  669. }
  670. ],
  671. 'correctly uses negative @r repeat attribute'
  672. );
  673. });
  674. QUnit.test('correctly handles negative @r repeat value for last S', function(assert) {
  675. const attributes = {
  676. startNumber: 0,
  677. sourceDuration: 15,
  678. timescale: 1000,
  679. periodIndex: 1,
  680. type: 'static',
  681. periodStart: 0
  682. };
  683. const segmentTimeline = [
  684. {
  685. t: 0,
  686. d: 6000
  687. },
  688. {
  689. d: 3000,
  690. r: -1
  691. }
  692. ];
  693. assert.deepEqual(
  694. parseTemplateInfo(attributes, segmentTimeline),
  695. [
  696. {
  697. number: 0,
  698. duration: 6,
  699. time: 0,
  700. timeline: 0
  701. },
  702. {
  703. number: 1,
  704. duration: 3,
  705. time: 6000,
  706. timeline: 0
  707. },
  708. {
  709. number: 2,
  710. duration: 3,
  711. time: 9000,
  712. timeline: 0
  713. },
  714. {
  715. number: 3,
  716. duration: 3,
  717. time: 12000,
  718. timeline: 0
  719. }
  720. ],
  721. 'correctly uses negative @r repeat attribute for last S'
  722. );
  723. });
  724. QUnit.module('segmentTemplate - type ="dynamic"');
  725. QUnit.test('correctly handles duration', function(assert) {
  726. const basicAttributes = {
  727. baseUrl: 'http://www.example.com/',
  728. type: 'dynamic',
  729. media: 'n-$Number$.m4s',
  730. minimumUpdatePeriod: 0,
  731. timescale: 1,
  732. NOW: 10000,
  733. clientOffset: 0,
  734. availabilityStartTime: 0,
  735. startNumber: 1,
  736. duration: 2,
  737. periodIndex: 1,
  738. periodStart: 0
  739. };
  740. assert.deepEqual(
  741. segmentsFromTemplate(basicAttributes, []),
  742. [{
  743. duration: 2,
  744. map: {
  745. resolvedUri: 'http://www.example.com/',
  746. uri: ''
  747. },
  748. number: 1,
  749. resolvedUri: 'http://www.example.com/n-1.m4s',
  750. timeline: 0,
  751. uri: 'n-1.m4s',
  752. presentationTime: 0
  753. }, {
  754. duration: 2,
  755. map: {
  756. resolvedUri: 'http://www.example.com/',
  757. uri: ''
  758. },
  759. number: 2,
  760. resolvedUri: 'http://www.example.com/n-2.m4s',
  761. timeline: 0,
  762. uri: 'n-2.m4s',
  763. presentationTime: 2
  764. }, {
  765. duration: 2,
  766. map: {
  767. resolvedUri: 'http://www.example.com/',
  768. uri: ''
  769. },
  770. number: 3,
  771. resolvedUri: 'http://www.example.com/n-3.m4s',
  772. timeline: 0,
  773. uri: 'n-3.m4s',
  774. presentationTime: 4
  775. }, {
  776. duration: 2,
  777. map: {
  778. resolvedUri: 'http://www.example.com/',
  779. uri: ''
  780. },
  781. number: 4,
  782. resolvedUri: 'http://www.example.com/n-4.m4s',
  783. timeline: 0,
  784. uri: 'n-4.m4s',
  785. presentationTime: 6
  786. }, {
  787. duration: 2,
  788. map: {
  789. resolvedUri: 'http://www.example.com/',
  790. uri: ''
  791. },
  792. number: 5,
  793. resolvedUri: 'http://www.example.com/n-5.m4s',
  794. timeline: 0,
  795. uri: 'n-5.m4s',
  796. presentationTime: 8
  797. }],
  798. 'segments correctly with basic settings'
  799. );
  800. assert.deepEqual(
  801. segmentsFromTemplate(Object.assign({}, basicAttributes, { startNumber: 10 }), []),
  802. [{
  803. duration: 2,
  804. map: {
  805. resolvedUri: 'http://www.example.com/',
  806. uri: ''
  807. },
  808. number: 10,
  809. resolvedUri: 'http://www.example.com/n-10.m4s',
  810. timeline: 0,
  811. uri: 'n-10.m4s',
  812. presentationTime: 0
  813. }, {
  814. duration: 2,
  815. map: {
  816. resolvedUri: 'http://www.example.com/',
  817. uri: ''
  818. },
  819. number: 11,
  820. resolvedUri: 'http://www.example.com/n-11.m4s',
  821. timeline: 0,
  822. uri: 'n-11.m4s',
  823. presentationTime: 2
  824. }, {
  825. duration: 2,
  826. map: {
  827. resolvedUri: 'http://www.example.com/',
  828. uri: ''
  829. },
  830. number: 12,
  831. resolvedUri: 'http://www.example.com/n-12.m4s',
  832. timeline: 0,
  833. uri: 'n-12.m4s',
  834. presentationTime: 4
  835. }, {
  836. duration: 2,
  837. map: {
  838. resolvedUri: 'http://www.example.com/',
  839. uri: ''
  840. },
  841. number: 13,
  842. resolvedUri: 'http://www.example.com/n-13.m4s',
  843. timeline: 0,
  844. uri: 'n-13.m4s',
  845. presentationTime: 6
  846. }, {
  847. duration: 2,
  848. map: {
  849. resolvedUri: 'http://www.example.com/',
  850. uri: ''
  851. },
  852. number: 14,
  853. resolvedUri: 'http://www.example.com/n-14.m4s',
  854. timeline: 0,
  855. uri: 'n-14.m4s',
  856. presentationTime: 8
  857. }],
  858. 'segments adjusted correctly based on @startNumber'
  859. );
  860. assert.deepEqual(
  861. segmentsFromTemplate(Object.assign(
  862. {}, basicAttributes,
  863. { availabilityStartTime: 4 }
  864. ), []),
  865. [{
  866. duration: 2,
  867. map: {
  868. resolvedUri: 'http://www.example.com/',
  869. uri: ''
  870. },
  871. number: 1,
  872. resolvedUri: 'http://www.example.com/n-1.m4s',
  873. timeline: 0,
  874. uri: 'n-1.m4s',
  875. presentationTime: 0
  876. }, {
  877. duration: 2,
  878. map: {
  879. resolvedUri: 'http://www.example.com/',
  880. uri: ''
  881. },
  882. number: 2,
  883. resolvedUri: 'http://www.example.com/n-2.m4s',
  884. timeline: 0,
  885. uri: 'n-2.m4s',
  886. presentationTime: 2
  887. }, {
  888. duration: 2,
  889. map: {
  890. resolvedUri: 'http://www.example.com/',
  891. uri: ''
  892. },
  893. number: 3,
  894. resolvedUri: 'http://www.example.com/n-3.m4s',
  895. timeline: 0,
  896. uri: 'n-3.m4s',
  897. presentationTime: 4
  898. }],
  899. 'segments correct with @availabilityStartTime set'
  900. );
  901. assert.deepEqual(
  902. segmentsFromTemplate(Object.assign(
  903. {}, basicAttributes,
  904. { availabilityStartTime: 2, periodStart: 4 }
  905. ), []),
  906. [{
  907. duration: 2,
  908. map: {
  909. resolvedUri: 'http://www.example.com/',
  910. uri: ''
  911. },
  912. number: 1,
  913. resolvedUri: 'http://www.example.com/n-1.m4s',
  914. timeline: 4,
  915. uri: 'n-1.m4s',
  916. presentationTime: 4
  917. }, {
  918. duration: 2,
  919. map: {
  920. resolvedUri: 'http://www.example.com/',
  921. uri: ''
  922. },
  923. number: 2,
  924. resolvedUri: 'http://www.example.com/n-2.m4s',
  925. timeline: 4,
  926. uri: 'n-2.m4s',
  927. presentationTime: 6
  928. }],
  929. 'segments correct with @availabilityStartTime and @start set'
  930. );
  931. assert.deepEqual(
  932. segmentsFromTemplate(Object.assign(
  933. {}, basicAttributes,
  934. { timeShiftBufferDepth: 4 }, []
  935. )),
  936. [{
  937. duration: 2,
  938. map: {
  939. resolvedUri: 'http://www.example.com/',
  940. uri: ''
  941. },
  942. number: 4,
  943. resolvedUri: 'http://www.example.com/n-4.m4s',
  944. timeline: 0,
  945. uri: 'n-4.m4s',
  946. presentationTime: 6
  947. }, {
  948. duration: 2,
  949. map: {
  950. resolvedUri: 'http://www.example.com/',
  951. uri: ''
  952. },
  953. number: 5,
  954. resolvedUri: 'http://www.example.com/n-5.m4s',
  955. timeline: 0,
  956. uri: 'n-5.m4s',
  957. presentationTime: 8
  958. }],
  959. 'segments correct with @timeShiftBufferDepth set'
  960. );
  961. assert.deepEqual(
  962. segmentsFromTemplate(Object.assign(
  963. {}, basicAttributes,
  964. { clientOffset: -2000 }, []
  965. )),
  966. [{
  967. duration: 2,
  968. map: {
  969. resolvedUri: 'http://www.example.com/',
  970. uri: ''
  971. },
  972. number: 1,
  973. resolvedUri: 'http://www.example.com/n-1.m4s',
  974. timeline: 0,
  975. uri: 'n-1.m4s',
  976. presentationTime: 0
  977. }, {
  978. duration: 2,
  979. map: {
  980. resolvedUri: 'http://www.example.com/',
  981. uri: ''
  982. },
  983. number: 2,
  984. resolvedUri: 'http://www.example.com/n-2.m4s',
  985. timeline: 0,
  986. uri: 'n-2.m4s',
  987. presentationTime: 2
  988. }, {
  989. duration: 2,
  990. map: {
  991. resolvedUri: 'http://www.example.com/',
  992. uri: ''
  993. },
  994. number: 3,
  995. resolvedUri: 'http://www.example.com/n-3.m4s',
  996. timeline: 0,
  997. uri: 'n-3.m4s',
  998. presentationTime: 4
  999. }, {
  1000. duration: 2,
  1001. map: {
  1002. resolvedUri: 'http://www.example.com/',
  1003. uri: ''
  1004. },
  1005. number: 4,
  1006. resolvedUri: 'http://www.example.com/n-4.m4s',
  1007. timeline: 0,
  1008. uri: 'n-4.m4s',
  1009. presentationTime: 6
  1010. }],
  1011. 'segments correct with given clientOffset'
  1012. );
  1013. assert.deepEqual(
  1014. segmentsFromTemplate(Object.assign(
  1015. {}, basicAttributes,
  1016. { endNumber: '4' }, []
  1017. )),
  1018. [{
  1019. duration: 2,
  1020. map: {
  1021. resolvedUri: 'http://www.example.com/',
  1022. uri: ''
  1023. },
  1024. number: 1,
  1025. resolvedUri: 'http://www.example.com/n-1.m4s',
  1026. timeline: 0,
  1027. uri: 'n-1.m4s',
  1028. presentationTime: 0
  1029. }, {
  1030. duration: 2,
  1031. map: {
  1032. resolvedUri: 'http://www.example.com/',
  1033. uri: ''
  1034. },
  1035. number: 2,
  1036. resolvedUri: 'http://www.example.com/n-2.m4s',
  1037. timeline: 0,
  1038. uri: 'n-2.m4s',
  1039. presentationTime: 2
  1040. }, {
  1041. duration: 2,
  1042. map: {
  1043. resolvedUri: 'http://www.example.com/',
  1044. uri: ''
  1045. },
  1046. number: 3,
  1047. resolvedUri: 'http://www.example.com/n-3.m4s',
  1048. timeline: 0,
  1049. uri: 'n-3.m4s',
  1050. presentationTime: 4
  1051. }, {
  1052. duration: 2,
  1053. map: {
  1054. resolvedUri: 'http://www.example.com/',
  1055. uri: ''
  1056. },
  1057. number: 4,
  1058. resolvedUri: 'http://www.example.com/n-4.m4s',
  1059. timeline: 0,
  1060. uri: 'n-4.m4s',
  1061. presentationTime: 6
  1062. }],
  1063. 'segments correct with endNumber'
  1064. );
  1065. });
  1066. QUnit.test('correctly handles duration with segmentTimeline', function(assert) {
  1067. const basicAttributes = {
  1068. baseUrl: 'http://www.example.com/',
  1069. type: 'dynamic',
  1070. media: 'n-$Number$.m4s',
  1071. minimumUpdatePeriod: 2,
  1072. timescale: 1,
  1073. NOW: 8000,
  1074. clientOffset: 0,
  1075. availabilityStartTime: 0,
  1076. startNumber: 1,
  1077. periodIndex: 1,
  1078. periodStart: 0
  1079. };
  1080. const segmentTimeline = [
  1081. {
  1082. t: 0,
  1083. d: 2,
  1084. r: 1
  1085. },
  1086. {
  1087. d: 2,
  1088. r: -1
  1089. }
  1090. ];
  1091. assert.deepEqual(
  1092. segmentsFromTemplate(basicAttributes, segmentTimeline),
  1093. [{
  1094. duration: 2,
  1095. map: {
  1096. resolvedUri: 'http://www.example.com/',
  1097. uri: ''
  1098. },
  1099. number: 1,
  1100. resolvedUri: 'http://www.example.com/n-1.m4s',
  1101. timeline: 0,
  1102. uri: 'n-1.m4s',
  1103. presentationTime: 0
  1104. }, {
  1105. duration: 2,
  1106. map: {
  1107. resolvedUri: 'http://www.example.com/',
  1108. uri: ''
  1109. },
  1110. number: 2,
  1111. resolvedUri: 'http://www.example.com/n-2.m4s',
  1112. timeline: 0,
  1113. uri: 'n-2.m4s',
  1114. presentationTime: 2
  1115. }, {
  1116. duration: 2,
  1117. map: {
  1118. resolvedUri: 'http://www.example.com/',
  1119. uri: ''
  1120. },
  1121. number: 3,
  1122. resolvedUri: 'http://www.example.com/n-3.m4s',
  1123. timeline: 0,
  1124. uri: 'n-3.m4s',
  1125. presentationTime: 4
  1126. }, {
  1127. duration: 2,
  1128. map: {
  1129. resolvedUri: 'http://www.example.com/',
  1130. uri: ''
  1131. },
  1132. number: 4,
  1133. resolvedUri: 'http://www.example.com/n-4.m4s',
  1134. timeline: 0,
  1135. uri: 'n-4.m4s',
  1136. presentationTime: 6
  1137. }, {
  1138. duration: 2,
  1139. map: {
  1140. resolvedUri: 'http://www.example.com/',
  1141. uri: ''
  1142. },
  1143. number: 5,
  1144. resolvedUri: 'http://www.example.com/n-5.m4s',
  1145. timeline: 0,
  1146. uri: 'n-5.m4s',
  1147. presentationTime: 8
  1148. }],
  1149. 'segments should fill until current time when r = -1 and @minimumUpdatePeriod > 0'
  1150. );
  1151. assert.deepEqual(
  1152. segmentsFromTemplate(Object.assign({}, basicAttributes, {clientOffset: -2000}), segmentTimeline),
  1153. [{
  1154. duration: 2,
  1155. map: {
  1156. resolvedUri: 'http://www.example.com/',
  1157. uri: ''
  1158. },
  1159. number: 1,
  1160. resolvedUri: 'http://www.example.com/n-1.m4s',
  1161. timeline: 0,
  1162. uri: 'n-1.m4s',
  1163. presentationTime: 0
  1164. }, {
  1165. duration: 2,
  1166. map: {
  1167. resolvedUri: 'http://www.example.com/',
  1168. uri: ''
  1169. },
  1170. number: 2,
  1171. resolvedUri: 'http://www.example.com/n-2.m4s',
  1172. timeline: 0,
  1173. uri: 'n-2.m4s',
  1174. presentationTime: 2
  1175. }, {
  1176. duration: 2,
  1177. map: {
  1178. resolvedUri: 'http://www.example.com/',
  1179. uri: ''
  1180. },
  1181. number: 3,
  1182. resolvedUri: 'http://www.example.com/n-3.m4s',
  1183. timeline: 0,
  1184. uri: 'n-3.m4s',
  1185. presentationTime: 4
  1186. }, {
  1187. duration: 2,
  1188. map: {
  1189. resolvedUri: 'http://www.example.com/',
  1190. uri: ''
  1191. },
  1192. number: 4,
  1193. resolvedUri: 'http://www.example.com/n-4.m4s',
  1194. timeline: 0,
  1195. uri: 'n-4.m4s',
  1196. presentationTime: 6
  1197. }],
  1198. 'segments should fill correctly when taking client offset into account'
  1199. );
  1200. const segmentTimelineShifted = [
  1201. {
  1202. t: 2,
  1203. d: 2,
  1204. r: 1
  1205. },
  1206. {
  1207. d: 2,
  1208. r: -1
  1209. }
  1210. ];
  1211. assert.deepEqual(
  1212. segmentsFromTemplate(basicAttributes, segmentTimelineShifted),
  1213. [{
  1214. duration: 2,
  1215. map: {
  1216. resolvedUri: 'http://www.example.com/',
  1217. uri: ''
  1218. },
  1219. number: 1,
  1220. resolvedUri: 'http://www.example.com/n-1.m4s',
  1221. timeline: 0,
  1222. uri: 'n-1.m4s',
  1223. presentationTime: 2
  1224. }, {
  1225. duration: 2,
  1226. map: {
  1227. resolvedUri: 'http://www.example.com/',
  1228. uri: ''
  1229. },
  1230. number: 2,
  1231. resolvedUri: 'http://www.example.com/n-2.m4s',
  1232. timeline: 0,
  1233. uri: 'n-2.m4s',
  1234. presentationTime: 4
  1235. }, {
  1236. duration: 2,
  1237. map: {
  1238. resolvedUri: 'http://www.example.com/',
  1239. uri: ''
  1240. },
  1241. number: 3,
  1242. resolvedUri: 'http://www.example.com/n-3.m4s',
  1243. timeline: 0,
  1244. uri: 'n-3.m4s',
  1245. presentationTime: 6
  1246. }, {
  1247. duration: 2,
  1248. map: {
  1249. resolvedUri: 'http://www.example.com/',
  1250. uri: ''
  1251. },
  1252. number: 4,
  1253. resolvedUri: 'http://www.example.com/n-4.m4s',
  1254. timeline: 0,
  1255. uri: 'n-4.m4s',
  1256. presentationTime: 8
  1257. }],
  1258. 'segments take into account different time value for first segment'
  1259. );
  1260. assert.deepEqual(
  1261. segmentsFromTemplate(
  1262. Object.assign({}, basicAttributes, {timescale: 2 }),
  1263. segmentTimelineShifted
  1264. ),
  1265. [{
  1266. duration: 1,
  1267. map: {
  1268. resolvedUri: 'http://www.example.com/',
  1269. uri: ''
  1270. },
  1271. number: 1,
  1272. resolvedUri: 'http://www.example.com/n-1.m4s',
  1273. timeline: 0,
  1274. uri: 'n-1.m4s',
  1275. presentationTime: 1
  1276. }, {
  1277. duration: 1,
  1278. map: {
  1279. resolvedUri: 'http://www.example.com/',
  1280. uri: ''
  1281. },
  1282. number: 2,
  1283. resolvedUri: 'http://www.example.com/n-2.m4s',
  1284. timeline: 0,
  1285. uri: 'n-2.m4s',
  1286. presentationTime: 2
  1287. }, {
  1288. duration: 1,
  1289. map: {
  1290. resolvedUri: 'http://www.example.com/',
  1291. uri: ''
  1292. },
  1293. number: 3,
  1294. resolvedUri: 'http://www.example.com/n-3.m4s',
  1295. timeline: 0,
  1296. uri: 'n-3.m4s',
  1297. presentationTime: 3
  1298. }, {
  1299. duration: 1,
  1300. map: {
  1301. resolvedUri: 'http://www.example.com/',
  1302. uri: ''
  1303. },
  1304. number: 4,
  1305. resolvedUri: 'http://www.example.com/n-4.m4s',
  1306. timeline: 0,
  1307. uri: 'n-4.m4s',
  1308. presentationTime: 4
  1309. }, {
  1310. duration: 1,
  1311. map: {
  1312. resolvedUri: 'http://www.example.com/',
  1313. uri: ''
  1314. },
  1315. number: 5,
  1316. resolvedUri: 'http://www.example.com/n-5.m4s',
  1317. timeline: 0,
  1318. uri: 'n-5.m4s',
  1319. presentationTime: 5
  1320. }, {
  1321. duration: 1,
  1322. map: {
  1323. resolvedUri: 'http://www.example.com/',
  1324. uri: ''
  1325. },
  1326. number: 6,
  1327. resolvedUri: 'http://www.example.com/n-6.m4s',
  1328. timeline: 0,
  1329. uri: 'n-6.m4s',
  1330. presentationTime: 6
  1331. }, {
  1332. duration: 1,
  1333. map: {
  1334. resolvedUri: 'http://www.example.com/',
  1335. uri: ''
  1336. },
  1337. number: 7,
  1338. resolvedUri: 'http://www.example.com/n-7.m4s',
  1339. timeline: 0,
  1340. uri: 'n-7.m4s',
  1341. presentationTime: 7
  1342. }, {
  1343. duration: 1,
  1344. map: {
  1345. resolvedUri: 'http://www.example.com/',
  1346. uri: ''
  1347. },
  1348. number: 8,
  1349. resolvedUri: 'http://www.example.com/n-8.m4s',
  1350. timeline: 0,
  1351. uri: 'n-8.m4s',
  1352. presentationTime: 8
  1353. }, {
  1354. duration: 1,
  1355. map: {
  1356. resolvedUri: 'http://www.example.com/',
  1357. uri: ''
  1358. },
  1359. number: 9,
  1360. resolvedUri: 'http://www.example.com/n-9.m4s',
  1361. timeline: 0,
  1362. uri: 'n-9.m4s',
  1363. presentationTime: 9
  1364. }],
  1365. 'segments take into account different time value for first segment with timescale'
  1366. );
  1367. });
  1368. QUnit.module('segmentTemplate - segmentsFromTemplate');
  1369. QUnit.test('constructs simple segment list and resolves uris', function(assert) {
  1370. const attributes = {
  1371. startNumber: 0,
  1372. duration: 6000,
  1373. sourceDuration: 16,
  1374. timescale: 1000,
  1375. bandwidth: 100,
  1376. id: 'Rep1',
  1377. initialization: {
  1378. sourceURL: '$RepresentationID$/$Bandwidth$/init.mp4'
  1379. },
  1380. media: '$RepresentationID$/$Bandwidth$/$Number%03d$-$Time%05d$.mp4',
  1381. periodIndex: 1,
  1382. baseUrl: 'https://example.com/',
  1383. type: 'static',
  1384. periodStart: 0
  1385. };
  1386. const segments = [
  1387. {
  1388. duration: 6,
  1389. map: {
  1390. resolvedUri: 'https://example.com/Rep1/100/init.mp4',
  1391. uri: 'Rep1/100/init.mp4'
  1392. },
  1393. resolvedUri: 'https://example.com/Rep1/100/000-00000.mp4',
  1394. timeline: 0,
  1395. uri: 'Rep1/100/000-00000.mp4',
  1396. number: 0,
  1397. presentationTime: 0
  1398. },
  1399. {
  1400. duration: 6,
  1401. map: {
  1402. resolvedUri: 'https://example.com/Rep1/100/init.mp4',
  1403. uri: 'Rep1/100/init.mp4'
  1404. },
  1405. resolvedUri: 'https://example.com/Rep1/100/001-06000.mp4',
  1406. timeline: 0,
  1407. uri: 'Rep1/100/001-06000.mp4',
  1408. number: 1,
  1409. presentationTime: 6
  1410. },
  1411. {
  1412. duration: 4,
  1413. map: {
  1414. resolvedUri: 'https://example.com/Rep1/100/init.mp4',
  1415. uri: 'Rep1/100/init.mp4'
  1416. },
  1417. resolvedUri: 'https://example.com/Rep1/100/002-12000.mp4',
  1418. timeline: 0,
  1419. uri: 'Rep1/100/002-12000.mp4',
  1420. number: 2,
  1421. presentationTime: 12
  1422. }
  1423. ];
  1424. assert.deepEqual(
  1425. segmentsFromTemplate(attributes, void 0), segments,
  1426. 'creates segments from template'
  1427. );
  1428. });
  1429. QUnit.test('constructs simple segment list and with <Initialization> node', function(assert) {
  1430. const attributes = {
  1431. startNumber: 0,
  1432. duration: 6000,
  1433. sourceDuration: 16,
  1434. timescale: 1000,
  1435. bandwidth: 100,
  1436. id: 'Rep1',
  1437. initialization: {
  1438. sourceURL: 'init.mp4',
  1439. range: '121-125'
  1440. },
  1441. media: '$RepresentationID$/$Bandwidth$/$Number%03d$-$Time%05d$.mp4',
  1442. periodIndex: 1,
  1443. baseUrl: 'https://example.com/',
  1444. type: 'static',
  1445. periodStart: 0
  1446. };
  1447. const segments = [
  1448. {
  1449. duration: 6,
  1450. map: {
  1451. resolvedUri: 'https://example.com/init.mp4',
  1452. uri: 'init.mp4',
  1453. byterange: {
  1454. length: 5,
  1455. offset: 121
  1456. }
  1457. },
  1458. resolvedUri: 'https://example.com/Rep1/100/000-00000.mp4',
  1459. timeline: 0,
  1460. uri: 'Rep1/100/000-00000.mp4',
  1461. number: 0,
  1462. presentationTime: 0
  1463. },
  1464. {
  1465. duration: 6,
  1466. map: {
  1467. resolvedUri: 'https://example.com/init.mp4',
  1468. uri: 'init.mp4',
  1469. byterange: {
  1470. length: 5,
  1471. offset: 121
  1472. }
  1473. },
  1474. resolvedUri: 'https://example.com/Rep1/100/001-06000.mp4',
  1475. timeline: 0,
  1476. uri: 'Rep1/100/001-06000.mp4',
  1477. number: 1,
  1478. presentationTime: 6
  1479. },
  1480. {
  1481. duration: 4,
  1482. map: {
  1483. resolvedUri: 'https://example.com/init.mp4',
  1484. uri: 'init.mp4',
  1485. byterange: {
  1486. length: 5,
  1487. offset: 121
  1488. }
  1489. },
  1490. resolvedUri: 'https://example.com/Rep1/100/002-12000.mp4',
  1491. timeline: 0,
  1492. uri: 'Rep1/100/002-12000.mp4',
  1493. number: 2,
  1494. presentationTime: 12
  1495. }
  1496. ];
  1497. assert.deepEqual(
  1498. segmentsFromTemplate(attributes, void 0), segments,
  1499. 'creates segments from template'
  1500. );
  1501. });
  1502. QUnit.test('multiperiod uses periodDuration when available', function(assert) {
  1503. const attributes = {
  1504. startNumber: 0,
  1505. duration: 6000,
  1506. sourceDuration: 12,
  1507. timescale: 1000,
  1508. bandwidth: 100,
  1509. id: 'Rep1',
  1510. initialization: {
  1511. sourceURL: '$RepresentationID$/$Bandwidth$/init.mp4'
  1512. },
  1513. media: '$RepresentationID$/$Bandwidth$/$Number%03d$-$Time%05d$.mp4',
  1514. periodIndex: 1,
  1515. // 6 second period should mean a single 6 second segment
  1516. periodDuration: 6,
  1517. baseUrl: 'https://example.com/',
  1518. type: 'static',
  1519. periodStart: 0
  1520. };
  1521. const segments = [
  1522. {
  1523. duration: 6,
  1524. map: {
  1525. resolvedUri: 'https://example.com/Rep1/100/init.mp4',
  1526. uri: 'Rep1/100/init.mp4'
  1527. },
  1528. resolvedUri: 'https://example.com/Rep1/100/000-00000.mp4',
  1529. timeline: 0,
  1530. uri: 'Rep1/100/000-00000.mp4',
  1531. number: 0,
  1532. presentationTime: 0
  1533. }
  1534. ];
  1535. assert.deepEqual(
  1536. segmentsFromTemplate(attributes, void 0), segments,
  1537. 'creates segments from template'
  1538. );
  1539. });