hls.test.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. const m3u8Parser = require('m3u8-parser');
  2. const fetch = require('node-fetch');
  3. const url = require('url');
  4. const { test } = require('@jest/globals');
  5. const HLS_SUBDIRECTORY = '/hls/';
  6. const PLAYLIST_NAME = 'stream.m3u8';
  7. const TEST_OWNCAST_INSTANCE = 'http://localhost:8080';
  8. const HLS_FETCH_ITERATIONS = 5;
  9. jest.setTimeout(40000);
  10. async function getPlaylist(urlString) {
  11. const response = await fetch(urlString);
  12. expect(response.status).toBe(200);
  13. const body = await response.text();
  14. var parser = new m3u8Parser.Parser();
  15. parser.push(body);
  16. parser.end();
  17. return parser.manifest;
  18. }
  19. function normalizeUrl(urlString, baseUrl) {
  20. let parsedString = url.parse(urlString);
  21. if (!parsedString.host) {
  22. const testInstanceRoot = url.parse(baseUrl);
  23. parsedString.protocol = testInstanceRoot.protocol;
  24. parsedString.host = testInstanceRoot.host;
  25. const filename = baseUrl.substring(baseUrl.lastIndexOf('/') + 1);
  26. parsedString.pathname =
  27. testInstanceRoot.pathname.replace(filename, '') + urlString;
  28. }
  29. return url.format(parsedString).toString();
  30. }
  31. // Iterate over an array of video segments and make sure they return back
  32. // valid status.
  33. async function validateSegments(segments) {
  34. for (let segment of segments) {
  35. const res = await fetch(segment);
  36. expect(res.status).toBe(200);
  37. }
  38. }
  39. describe('fetch and parse HLS', () => {
  40. const masterPlaylistUrl = `${TEST_OWNCAST_INSTANCE}${HLS_SUBDIRECTORY}${PLAYLIST_NAME}`;
  41. var masterPlaylist;
  42. var mediaPlaylistUrl;
  43. test('fetch master playlist', async (done) => {
  44. try {
  45. masterPlaylist = await getPlaylist(masterPlaylistUrl);
  46. } catch (e) {
  47. console.error('error fetching and parsing master playlist', e);
  48. }
  49. done();
  50. });
  51. test('verify there is a media playlist', () => {
  52. // Master playlist should have at least one media playlist.
  53. expect(masterPlaylist.playlists.length).toBe(1);
  54. try {
  55. mediaPlaylistUrl = normalizeUrl(
  56. masterPlaylist.playlists[0].uri,
  57. masterPlaylistUrl
  58. );
  59. } catch (e) {
  60. console.error('error fetching and parsing media playlist', e);
  61. }
  62. });
  63. test('verify there are segments', async (done) => {
  64. let playlist;
  65. try {
  66. playlist = await getPlaylist(mediaPlaylistUrl);
  67. } catch (e) {
  68. console.error('error verifying segments in media playlist', e);
  69. }
  70. const segments = playlist.segments;
  71. expect(segments.length).toBeGreaterThan(0);
  72. done();
  73. });
  74. // Iterate over segments and make sure they change.
  75. // Use the reported duration of the segment to wait to
  76. // fetch another just like a real HLS player would do.
  77. var lastSegmentUrl;
  78. for (let i = 0; i < HLS_FETCH_ITERATIONS; i++) {
  79. test('fetch and monitor media playlist segments ' + i, async (done) => {
  80. await new Promise((r) => setTimeout(r, 5000));
  81. try {
  82. var playlist = await getPlaylist(mediaPlaylistUrl);
  83. } catch (e) {
  84. console.error('error updating media playlist', mediaPlaylistUrl, e);
  85. }
  86. const segments = playlist.segments;
  87. const segment = segments[segments.length - 1];
  88. expect(segment.uri).not.toBe(lastSegmentUrl);
  89. try {
  90. var segmentUrl = normalizeUrl(segment.uri, mediaPlaylistUrl);
  91. await validateSegments([segmentUrl]);
  92. } catch (e) {
  93. console.error('unable to validate HLS segment', segmentUrl, e);
  94. }
  95. lastSegmentUrl = segment.uri;
  96. done();
  97. });
  98. }
  99. });