segmentTemplate.test.js 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607
  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.skip(
  725. 'detects discontinuity when @t time is greater than expected start time',
  726. function(assert) {
  727. }
  728. );
  729. QUnit.module('segmentTemplate - type ="dynamic"');
  730. QUnit.test('correctly handles duration', function(assert) {
  731. const basicAttributes = {
  732. baseUrl: 'http://www.example.com/',
  733. type: 'dynamic',
  734. media: 'n-$Number$.m4s',
  735. minimumUpdatePeriod: 0,
  736. timescale: 1,
  737. NOW: 10000,
  738. clientOffset: 0,
  739. availabilityStartTime: 0,
  740. startNumber: 1,
  741. duration: 2,
  742. periodIndex: 1,
  743. periodStart: 0
  744. };
  745. assert.deepEqual(
  746. segmentsFromTemplate(basicAttributes, []),
  747. [{
  748. duration: 2,
  749. map: {
  750. resolvedUri: 'http://www.example.com/',
  751. uri: ''
  752. },
  753. number: 1,
  754. resolvedUri: 'http://www.example.com/n-1.m4s',
  755. timeline: 0,
  756. uri: 'n-1.m4s',
  757. presentationTime: 0
  758. }, {
  759. duration: 2,
  760. map: {
  761. resolvedUri: 'http://www.example.com/',
  762. uri: ''
  763. },
  764. number: 2,
  765. resolvedUri: 'http://www.example.com/n-2.m4s',
  766. timeline: 0,
  767. uri: 'n-2.m4s',
  768. presentationTime: 2
  769. }, {
  770. duration: 2,
  771. map: {
  772. resolvedUri: 'http://www.example.com/',
  773. uri: ''
  774. },
  775. number: 3,
  776. resolvedUri: 'http://www.example.com/n-3.m4s',
  777. timeline: 0,
  778. uri: 'n-3.m4s',
  779. presentationTime: 4
  780. }, {
  781. duration: 2,
  782. map: {
  783. resolvedUri: 'http://www.example.com/',
  784. uri: ''
  785. },
  786. number: 4,
  787. resolvedUri: 'http://www.example.com/n-4.m4s',
  788. timeline: 0,
  789. uri: 'n-4.m4s',
  790. presentationTime: 6
  791. }, {
  792. duration: 2,
  793. map: {
  794. resolvedUri: 'http://www.example.com/',
  795. uri: ''
  796. },
  797. number: 5,
  798. resolvedUri: 'http://www.example.com/n-5.m4s',
  799. timeline: 0,
  800. uri: 'n-5.m4s',
  801. presentationTime: 8
  802. }],
  803. 'segments correctly with basic settings'
  804. );
  805. assert.deepEqual(
  806. segmentsFromTemplate(Object.assign({}, basicAttributes, { startNumber: 10 }), []),
  807. [{
  808. duration: 2,
  809. map: {
  810. resolvedUri: 'http://www.example.com/',
  811. uri: ''
  812. },
  813. number: 10,
  814. resolvedUri: 'http://www.example.com/n-10.m4s',
  815. timeline: 0,
  816. uri: 'n-10.m4s',
  817. presentationTime: 0
  818. }, {
  819. duration: 2,
  820. map: {
  821. resolvedUri: 'http://www.example.com/',
  822. uri: ''
  823. },
  824. number: 11,
  825. resolvedUri: 'http://www.example.com/n-11.m4s',
  826. timeline: 0,
  827. uri: 'n-11.m4s',
  828. presentationTime: 2
  829. }, {
  830. duration: 2,
  831. map: {
  832. resolvedUri: 'http://www.example.com/',
  833. uri: ''
  834. },
  835. number: 12,
  836. resolvedUri: 'http://www.example.com/n-12.m4s',
  837. timeline: 0,
  838. uri: 'n-12.m4s',
  839. presentationTime: 4
  840. }, {
  841. duration: 2,
  842. map: {
  843. resolvedUri: 'http://www.example.com/',
  844. uri: ''
  845. },
  846. number: 13,
  847. resolvedUri: 'http://www.example.com/n-13.m4s',
  848. timeline: 0,
  849. uri: 'n-13.m4s',
  850. presentationTime: 6
  851. }, {
  852. duration: 2,
  853. map: {
  854. resolvedUri: 'http://www.example.com/',
  855. uri: ''
  856. },
  857. number: 14,
  858. resolvedUri: 'http://www.example.com/n-14.m4s',
  859. timeline: 0,
  860. uri: 'n-14.m4s',
  861. presentationTime: 8
  862. }],
  863. 'segments adjusted correctly based on @startNumber'
  864. );
  865. assert.deepEqual(
  866. segmentsFromTemplate(Object.assign(
  867. {}, basicAttributes,
  868. { availabilityStartTime: 4 }
  869. ), []),
  870. [{
  871. duration: 2,
  872. map: {
  873. resolvedUri: 'http://www.example.com/',
  874. uri: ''
  875. },
  876. number: 1,
  877. resolvedUri: 'http://www.example.com/n-1.m4s',
  878. timeline: 0,
  879. uri: 'n-1.m4s',
  880. presentationTime: 0
  881. }, {
  882. duration: 2,
  883. map: {
  884. resolvedUri: 'http://www.example.com/',
  885. uri: ''
  886. },
  887. number: 2,
  888. resolvedUri: 'http://www.example.com/n-2.m4s',
  889. timeline: 0,
  890. uri: 'n-2.m4s',
  891. presentationTime: 2
  892. }, {
  893. duration: 2,
  894. map: {
  895. resolvedUri: 'http://www.example.com/',
  896. uri: ''
  897. },
  898. number: 3,
  899. resolvedUri: 'http://www.example.com/n-3.m4s',
  900. timeline: 0,
  901. uri: 'n-3.m4s',
  902. presentationTime: 4
  903. }],
  904. 'segments correct with @availabilityStartTime set'
  905. );
  906. assert.deepEqual(
  907. segmentsFromTemplate(Object.assign(
  908. {}, basicAttributes,
  909. { availabilityStartTime: 2, periodStart: 4 }
  910. ), []),
  911. [{
  912. duration: 2,
  913. map: {
  914. resolvedUri: 'http://www.example.com/',
  915. uri: ''
  916. },
  917. number: 1,
  918. resolvedUri: 'http://www.example.com/n-1.m4s',
  919. timeline: 4,
  920. uri: 'n-1.m4s',
  921. presentationTime: 4
  922. }, {
  923. duration: 2,
  924. map: {
  925. resolvedUri: 'http://www.example.com/',
  926. uri: ''
  927. },
  928. number: 2,
  929. resolvedUri: 'http://www.example.com/n-2.m4s',
  930. timeline: 4,
  931. uri: 'n-2.m4s',
  932. presentationTime: 6
  933. }],
  934. 'segments correct with @availabilityStartTime and @start set'
  935. );
  936. assert.deepEqual(
  937. segmentsFromTemplate(Object.assign(
  938. {}, basicAttributes,
  939. { timeShiftBufferDepth: 4 }, []
  940. )),
  941. [{
  942. duration: 2,
  943. map: {
  944. resolvedUri: 'http://www.example.com/',
  945. uri: ''
  946. },
  947. number: 4,
  948. resolvedUri: 'http://www.example.com/n-4.m4s',
  949. timeline: 0,
  950. uri: 'n-4.m4s',
  951. presentationTime: 6
  952. }, {
  953. duration: 2,
  954. map: {
  955. resolvedUri: 'http://www.example.com/',
  956. uri: ''
  957. },
  958. number: 5,
  959. resolvedUri: 'http://www.example.com/n-5.m4s',
  960. timeline: 0,
  961. uri: 'n-5.m4s',
  962. presentationTime: 8
  963. }],
  964. 'segments correct with @timeShiftBufferDepth set'
  965. );
  966. assert.deepEqual(
  967. segmentsFromTemplate(Object.assign(
  968. {}, basicAttributes,
  969. { clientOffset: -2000 }, []
  970. )),
  971. [{
  972. duration: 2,
  973. map: {
  974. resolvedUri: 'http://www.example.com/',
  975. uri: ''
  976. },
  977. number: 1,
  978. resolvedUri: 'http://www.example.com/n-1.m4s',
  979. timeline: 0,
  980. uri: 'n-1.m4s',
  981. presentationTime: 0
  982. }, {
  983. duration: 2,
  984. map: {
  985. resolvedUri: 'http://www.example.com/',
  986. uri: ''
  987. },
  988. number: 2,
  989. resolvedUri: 'http://www.example.com/n-2.m4s',
  990. timeline: 0,
  991. uri: 'n-2.m4s',
  992. presentationTime: 2
  993. }, {
  994. duration: 2,
  995. map: {
  996. resolvedUri: 'http://www.example.com/',
  997. uri: ''
  998. },
  999. number: 3,
  1000. resolvedUri: 'http://www.example.com/n-3.m4s',
  1001. timeline: 0,
  1002. uri: 'n-3.m4s',
  1003. presentationTime: 4
  1004. }, {
  1005. duration: 2,
  1006. map: {
  1007. resolvedUri: 'http://www.example.com/',
  1008. uri: ''
  1009. },
  1010. number: 4,
  1011. resolvedUri: 'http://www.example.com/n-4.m4s',
  1012. timeline: 0,
  1013. uri: 'n-4.m4s',
  1014. presentationTime: 6
  1015. }],
  1016. 'segments correct with given clientOffset'
  1017. );
  1018. assert.deepEqual(
  1019. segmentsFromTemplate(Object.assign(
  1020. {}, basicAttributes,
  1021. { endNumber: '4' }, []
  1022. )),
  1023. [{
  1024. duration: 2,
  1025. map: {
  1026. resolvedUri: 'http://www.example.com/',
  1027. uri: ''
  1028. },
  1029. number: 1,
  1030. resolvedUri: 'http://www.example.com/n-1.m4s',
  1031. timeline: 0,
  1032. uri: 'n-1.m4s',
  1033. presentationTime: 0
  1034. }, {
  1035. duration: 2,
  1036. map: {
  1037. resolvedUri: 'http://www.example.com/',
  1038. uri: ''
  1039. },
  1040. number: 2,
  1041. resolvedUri: 'http://www.example.com/n-2.m4s',
  1042. timeline: 0,
  1043. uri: 'n-2.m4s',
  1044. presentationTime: 2
  1045. }, {
  1046. duration: 2,
  1047. map: {
  1048. resolvedUri: 'http://www.example.com/',
  1049. uri: ''
  1050. },
  1051. number: 3,
  1052. resolvedUri: 'http://www.example.com/n-3.m4s',
  1053. timeline: 0,
  1054. uri: 'n-3.m4s',
  1055. presentationTime: 4
  1056. }, {
  1057. duration: 2,
  1058. map: {
  1059. resolvedUri: 'http://www.example.com/',
  1060. uri: ''
  1061. },
  1062. number: 4,
  1063. resolvedUri: 'http://www.example.com/n-4.m4s',
  1064. timeline: 0,
  1065. uri: 'n-4.m4s',
  1066. presentationTime: 6
  1067. }],
  1068. 'segments correct with endNumber'
  1069. );
  1070. });
  1071. QUnit.test('correctly handles duration with segmentTimeline', function(assert) {
  1072. const basicAttributes = {
  1073. baseUrl: 'http://www.example.com/',
  1074. type: 'dynamic',
  1075. media: 'n-$Number$.m4s',
  1076. minimumUpdatePeriod: 2,
  1077. timescale: 1,
  1078. NOW: 8000,
  1079. clientOffset: 0,
  1080. availabilityStartTime: 0,
  1081. startNumber: 1,
  1082. periodIndex: 1,
  1083. periodStart: 0
  1084. };
  1085. const segmentTimeline = [
  1086. {
  1087. t: 0,
  1088. d: 2,
  1089. r: 1
  1090. },
  1091. {
  1092. d: 2,
  1093. r: -1
  1094. }
  1095. ];
  1096. assert.deepEqual(
  1097. segmentsFromTemplate(basicAttributes, segmentTimeline),
  1098. [{
  1099. duration: 2,
  1100. map: {
  1101. resolvedUri: 'http://www.example.com/',
  1102. uri: ''
  1103. },
  1104. number: 1,
  1105. resolvedUri: 'http://www.example.com/n-1.m4s',
  1106. timeline: 0,
  1107. uri: 'n-1.m4s',
  1108. presentationTime: 0
  1109. }, {
  1110. duration: 2,
  1111. map: {
  1112. resolvedUri: 'http://www.example.com/',
  1113. uri: ''
  1114. },
  1115. number: 2,
  1116. resolvedUri: 'http://www.example.com/n-2.m4s',
  1117. timeline: 0,
  1118. uri: 'n-2.m4s',
  1119. presentationTime: 2
  1120. }, {
  1121. duration: 2,
  1122. map: {
  1123. resolvedUri: 'http://www.example.com/',
  1124. uri: ''
  1125. },
  1126. number: 3,
  1127. resolvedUri: 'http://www.example.com/n-3.m4s',
  1128. timeline: 0,
  1129. uri: 'n-3.m4s',
  1130. presentationTime: 4
  1131. }, {
  1132. duration: 2,
  1133. map: {
  1134. resolvedUri: 'http://www.example.com/',
  1135. uri: ''
  1136. },
  1137. number: 4,
  1138. resolvedUri: 'http://www.example.com/n-4.m4s',
  1139. timeline: 0,
  1140. uri: 'n-4.m4s',
  1141. presentationTime: 6
  1142. }, {
  1143. duration: 2,
  1144. map: {
  1145. resolvedUri: 'http://www.example.com/',
  1146. uri: ''
  1147. },
  1148. number: 5,
  1149. resolvedUri: 'http://www.example.com/n-5.m4s',
  1150. timeline: 0,
  1151. uri: 'n-5.m4s',
  1152. presentationTime: 8
  1153. }],
  1154. 'segments should fill until current time when r = -1 and @minimumUpdatePeriod > 0'
  1155. );
  1156. assert.deepEqual(
  1157. segmentsFromTemplate(Object.assign({}, basicAttributes, {clientOffset: -2000}), segmentTimeline),
  1158. [{
  1159. duration: 2,
  1160. map: {
  1161. resolvedUri: 'http://www.example.com/',
  1162. uri: ''
  1163. },
  1164. number: 1,
  1165. resolvedUri: 'http://www.example.com/n-1.m4s',
  1166. timeline: 0,
  1167. uri: 'n-1.m4s',
  1168. presentationTime: 0
  1169. }, {
  1170. duration: 2,
  1171. map: {
  1172. resolvedUri: 'http://www.example.com/',
  1173. uri: ''
  1174. },
  1175. number: 2,
  1176. resolvedUri: 'http://www.example.com/n-2.m4s',
  1177. timeline: 0,
  1178. uri: 'n-2.m4s',
  1179. presentationTime: 2
  1180. }, {
  1181. duration: 2,
  1182. map: {
  1183. resolvedUri: 'http://www.example.com/',
  1184. uri: ''
  1185. },
  1186. number: 3,
  1187. resolvedUri: 'http://www.example.com/n-3.m4s',
  1188. timeline: 0,
  1189. uri: 'n-3.m4s',
  1190. presentationTime: 4
  1191. }, {
  1192. duration: 2,
  1193. map: {
  1194. resolvedUri: 'http://www.example.com/',
  1195. uri: ''
  1196. },
  1197. number: 4,
  1198. resolvedUri: 'http://www.example.com/n-4.m4s',
  1199. timeline: 0,
  1200. uri: 'n-4.m4s',
  1201. presentationTime: 6
  1202. }],
  1203. 'segments should fill correctly when taking client offset into account'
  1204. );
  1205. const segmentTimelineShifted = [
  1206. {
  1207. t: 2,
  1208. d: 2,
  1209. r: 1
  1210. },
  1211. {
  1212. d: 2,
  1213. r: -1
  1214. }
  1215. ];
  1216. assert.deepEqual(
  1217. segmentsFromTemplate(basicAttributes, segmentTimelineShifted),
  1218. [{
  1219. duration: 2,
  1220. map: {
  1221. resolvedUri: 'http://www.example.com/',
  1222. uri: ''
  1223. },
  1224. number: 1,
  1225. resolvedUri: 'http://www.example.com/n-1.m4s',
  1226. timeline: 0,
  1227. uri: 'n-1.m4s',
  1228. presentationTime: 2
  1229. }, {
  1230. duration: 2,
  1231. map: {
  1232. resolvedUri: 'http://www.example.com/',
  1233. uri: ''
  1234. },
  1235. number: 2,
  1236. resolvedUri: 'http://www.example.com/n-2.m4s',
  1237. timeline: 0,
  1238. uri: 'n-2.m4s',
  1239. presentationTime: 4
  1240. }, {
  1241. duration: 2,
  1242. map: {
  1243. resolvedUri: 'http://www.example.com/',
  1244. uri: ''
  1245. },
  1246. number: 3,
  1247. resolvedUri: 'http://www.example.com/n-3.m4s',
  1248. timeline: 0,
  1249. uri: 'n-3.m4s',
  1250. presentationTime: 6
  1251. }, {
  1252. duration: 2,
  1253. map: {
  1254. resolvedUri: 'http://www.example.com/',
  1255. uri: ''
  1256. },
  1257. number: 4,
  1258. resolvedUri: 'http://www.example.com/n-4.m4s',
  1259. timeline: 0,
  1260. uri: 'n-4.m4s',
  1261. presentationTime: 8
  1262. }],
  1263. 'segments take into account different time value for first segment'
  1264. );
  1265. assert.deepEqual(
  1266. segmentsFromTemplate(
  1267. Object.assign({}, basicAttributes, {timescale: 2 }),
  1268. segmentTimelineShifted
  1269. ),
  1270. [{
  1271. duration: 1,
  1272. map: {
  1273. resolvedUri: 'http://www.example.com/',
  1274. uri: ''
  1275. },
  1276. number: 1,
  1277. resolvedUri: 'http://www.example.com/n-1.m4s',
  1278. timeline: 0,
  1279. uri: 'n-1.m4s',
  1280. presentationTime: 1
  1281. }, {
  1282. duration: 1,
  1283. map: {
  1284. resolvedUri: 'http://www.example.com/',
  1285. uri: ''
  1286. },
  1287. number: 2,
  1288. resolvedUri: 'http://www.example.com/n-2.m4s',
  1289. timeline: 0,
  1290. uri: 'n-2.m4s',
  1291. presentationTime: 2
  1292. }, {
  1293. duration: 1,
  1294. map: {
  1295. resolvedUri: 'http://www.example.com/',
  1296. uri: ''
  1297. },
  1298. number: 3,
  1299. resolvedUri: 'http://www.example.com/n-3.m4s',
  1300. timeline: 0,
  1301. uri: 'n-3.m4s',
  1302. presentationTime: 3
  1303. }, {
  1304. duration: 1,
  1305. map: {
  1306. resolvedUri: 'http://www.example.com/',
  1307. uri: ''
  1308. },
  1309. number: 4,
  1310. resolvedUri: 'http://www.example.com/n-4.m4s',
  1311. timeline: 0,
  1312. uri: 'n-4.m4s',
  1313. presentationTime: 4
  1314. }, {
  1315. duration: 1,
  1316. map: {
  1317. resolvedUri: 'http://www.example.com/',
  1318. uri: ''
  1319. },
  1320. number: 5,
  1321. resolvedUri: 'http://www.example.com/n-5.m4s',
  1322. timeline: 0,
  1323. uri: 'n-5.m4s',
  1324. presentationTime: 5
  1325. }, {
  1326. duration: 1,
  1327. map: {
  1328. resolvedUri: 'http://www.example.com/',
  1329. uri: ''
  1330. },
  1331. number: 6,
  1332. resolvedUri: 'http://www.example.com/n-6.m4s',
  1333. timeline: 0,
  1334. uri: 'n-6.m4s',
  1335. presentationTime: 6
  1336. }, {
  1337. duration: 1,
  1338. map: {
  1339. resolvedUri: 'http://www.example.com/',
  1340. uri: ''
  1341. },
  1342. number: 7,
  1343. resolvedUri: 'http://www.example.com/n-7.m4s',
  1344. timeline: 0,
  1345. uri: 'n-7.m4s',
  1346. presentationTime: 7
  1347. }, {
  1348. duration: 1,
  1349. map: {
  1350. resolvedUri: 'http://www.example.com/',
  1351. uri: ''
  1352. },
  1353. number: 8,
  1354. resolvedUri: 'http://www.example.com/n-8.m4s',
  1355. timeline: 0,
  1356. uri: 'n-8.m4s',
  1357. presentationTime: 8
  1358. }, {
  1359. duration: 1,
  1360. map: {
  1361. resolvedUri: 'http://www.example.com/',
  1362. uri: ''
  1363. },
  1364. number: 9,
  1365. resolvedUri: 'http://www.example.com/n-9.m4s',
  1366. timeline: 0,
  1367. uri: 'n-9.m4s',
  1368. presentationTime: 9
  1369. }],
  1370. 'segments take into account different time value for first segment with timescale'
  1371. );
  1372. });
  1373. QUnit.module('segmentTemplate - segmentsFromTemplate');
  1374. QUnit.test('constructs simple segment list and resolves uris', function(assert) {
  1375. const attributes = {
  1376. startNumber: 0,
  1377. duration: 6000,
  1378. sourceDuration: 16,
  1379. timescale: 1000,
  1380. bandwidth: 100,
  1381. id: 'Rep1',
  1382. initialization: {
  1383. sourceURL: '$RepresentationID$/$Bandwidth$/init.mp4'
  1384. },
  1385. media: '$RepresentationID$/$Bandwidth$/$Number%03d$-$Time%05d$.mp4',
  1386. periodIndex: 1,
  1387. baseUrl: 'https://example.com/',
  1388. type: 'static',
  1389. periodStart: 0
  1390. };
  1391. const segments = [
  1392. {
  1393. duration: 6,
  1394. map: {
  1395. resolvedUri: 'https://example.com/Rep1/100/init.mp4',
  1396. uri: 'Rep1/100/init.mp4'
  1397. },
  1398. resolvedUri: 'https://example.com/Rep1/100/000-00000.mp4',
  1399. timeline: 0,
  1400. uri: 'Rep1/100/000-00000.mp4',
  1401. number: 0,
  1402. presentationTime: 0
  1403. },
  1404. {
  1405. duration: 6,
  1406. map: {
  1407. resolvedUri: 'https://example.com/Rep1/100/init.mp4',
  1408. uri: 'Rep1/100/init.mp4'
  1409. },
  1410. resolvedUri: 'https://example.com/Rep1/100/001-06000.mp4',
  1411. timeline: 0,
  1412. uri: 'Rep1/100/001-06000.mp4',
  1413. number: 1,
  1414. presentationTime: 6
  1415. },
  1416. {
  1417. duration: 4,
  1418. map: {
  1419. resolvedUri: 'https://example.com/Rep1/100/init.mp4',
  1420. uri: 'Rep1/100/init.mp4'
  1421. },
  1422. resolvedUri: 'https://example.com/Rep1/100/002-12000.mp4',
  1423. timeline: 0,
  1424. uri: 'Rep1/100/002-12000.mp4',
  1425. number: 2,
  1426. presentationTime: 12
  1427. }
  1428. ];
  1429. assert.deepEqual(
  1430. segmentsFromTemplate(attributes, void 0), segments,
  1431. 'creates segments from template'
  1432. );
  1433. });
  1434. QUnit.test('constructs simple segment list and with <Initialization> node', function(assert) {
  1435. const attributes = {
  1436. startNumber: 0,
  1437. duration: 6000,
  1438. sourceDuration: 16,
  1439. timescale: 1000,
  1440. bandwidth: 100,
  1441. id: 'Rep1',
  1442. initialization: {
  1443. sourceURL: 'init.mp4',
  1444. range: '121-125'
  1445. },
  1446. media: '$RepresentationID$/$Bandwidth$/$Number%03d$-$Time%05d$.mp4',
  1447. periodIndex: 1,
  1448. baseUrl: 'https://example.com/',
  1449. type: 'static',
  1450. periodStart: 0
  1451. };
  1452. const segments = [
  1453. {
  1454. duration: 6,
  1455. map: {
  1456. resolvedUri: 'https://example.com/init.mp4',
  1457. uri: 'init.mp4',
  1458. byterange: {
  1459. length: 5,
  1460. offset: 121
  1461. }
  1462. },
  1463. resolvedUri: 'https://example.com/Rep1/100/000-00000.mp4',
  1464. timeline: 0,
  1465. uri: 'Rep1/100/000-00000.mp4',
  1466. number: 0,
  1467. presentationTime: 0
  1468. },
  1469. {
  1470. duration: 6,
  1471. map: {
  1472. resolvedUri: 'https://example.com/init.mp4',
  1473. uri: 'init.mp4',
  1474. byterange: {
  1475. length: 5,
  1476. offset: 121
  1477. }
  1478. },
  1479. resolvedUri: 'https://example.com/Rep1/100/001-06000.mp4',
  1480. timeline: 0,
  1481. uri: 'Rep1/100/001-06000.mp4',
  1482. number: 1,
  1483. presentationTime: 6
  1484. },
  1485. {
  1486. duration: 4,
  1487. map: {
  1488. resolvedUri: 'https://example.com/init.mp4',
  1489. uri: 'init.mp4',
  1490. byterange: {
  1491. length: 5,
  1492. offset: 121
  1493. }
  1494. },
  1495. resolvedUri: 'https://example.com/Rep1/100/002-12000.mp4',
  1496. timeline: 0,
  1497. uri: 'Rep1/100/002-12000.mp4',
  1498. number: 2,
  1499. presentationTime: 12
  1500. }
  1501. ];
  1502. assert.deepEqual(
  1503. segmentsFromTemplate(attributes, void 0), segments,
  1504. 'creates segments from template'
  1505. );
  1506. });
  1507. QUnit.test('multiperiod uses periodDuration when available', function(assert) {
  1508. const attributes = {
  1509. startNumber: 0,
  1510. duration: 6000,
  1511. sourceDuration: 12,
  1512. timescale: 1000,
  1513. bandwidth: 100,
  1514. id: 'Rep1',
  1515. initialization: {
  1516. sourceURL: '$RepresentationID$/$Bandwidth$/init.mp4'
  1517. },
  1518. media: '$RepresentationID$/$Bandwidth$/$Number%03d$-$Time%05d$.mp4',
  1519. periodIndex: 1,
  1520. // 6 second period should mean a single 6 second segment
  1521. periodDuration: 6,
  1522. baseUrl: 'https://example.com/',
  1523. type: 'static',
  1524. periodStart: 0
  1525. };
  1526. const segments = [
  1527. {
  1528. duration: 6,
  1529. map: {
  1530. resolvedUri: 'https://example.com/Rep1/100/init.mp4',
  1531. uri: 'Rep1/100/init.mp4'
  1532. },
  1533. resolvedUri: 'https://example.com/Rep1/100/000-00000.mp4',
  1534. timeline: 0,
  1535. uri: 'Rep1/100/000-00000.mp4',
  1536. number: 0,
  1537. presentationTime: 0
  1538. }
  1539. ];
  1540. assert.deepEqual(
  1541. segmentsFromTemplate(attributes, void 0), segments,
  1542. 'creates segments from template'
  1543. );
  1544. });