Fix whitespace
All files should use 2-space indenting.
Showing
9 changed files
with
573 additions
and
571 deletions
1 | <!DOCTYPE html> | 1 | <!DOCTYPE html> |
2 | <html> | 2 | <html> |
3 | <head> | 3 | <head> |
4 | <meta charset="utf-8"> | 4 | <meta charset="utf-8"> |
5 | <title>video.js HLS Plugin Example</title> | 5 | <title>video.js HLS Plugin Example</title> |
6 | 6 | ||
7 | <link href="node_modules/video.js/video-js.css" rel="stylesheet"> | 7 | <link href="node_modules/video.js/video-js.css" rel="stylesheet"> |
8 | 8 | ||
... | @@ -52,7 +52,7 @@ | ... | @@ -52,7 +52,7 @@ |
52 | videojs.options.flash.swf = 'node_modules/videojs-media-sources/video-js-with-mse.swf'; | 52 | videojs.options.flash.swf = 'node_modules/videojs-media-sources/video-js-with-mse.swf'; |
53 | video = videojs('video',{},function(){ | 53 | video = videojs('video',{},function(){ |
54 | this.playbackController = new window.videojs.hls.HLSPlaybackController(this); | 54 | this.playbackController = new window.videojs.hls.HLSPlaybackController(this); |
55 | this.playbackController.loadManifest("http://localhost:7070/test/basic-playback/zencoder/gogo/manifest.m3u8",function(data){console.log(data)}); | 55 | this.playbackController.loadManifest('http://localhost:7070/test/basic-playback/zencoder/gogo/manifest.m3u8',function(data){console.log(data)}); |
56 | }); | 56 | }); |
57 | </script> | 57 | </script> |
58 | 58 | ... | ... |
1 | (function (window) { | 1 | (function (window) { |
2 | window.videojs.hls.HLSPlaybackController = function (vjsPlayerReference) { | 2 | window.videojs.hls.HLSPlaybackController = function (vjsPlayerReference) { |
3 | var ManifestController = window.videojs.hls.ManifestController; | 3 | var ManifestController = window.videojs.hls.ManifestController; |
4 | var SegmentController = window.videojs.hls.SegmentController; | 4 | var SegmentController = window.videojs.hls.SegmentController; |
5 | var MediaSource = window.videojs.MediaSource; | 5 | var MediaSource = window.videojs.MediaSource; |
6 | var SegmentParser = window.videojs.hls.SegmentParser; | 6 | var SegmentParser = window.videojs.hls.SegmentParser; |
7 | var M3U8 = window.videojs.hls.M3U8; | 7 | var M3U8 = window.videojs.hls.M3U8; |
8 | 8 | ||
9 | var self = this; | 9 | var self = this; |
10 | 10 | ||
11 | self.player = vjsPlayerReference; | 11 | self.player = vjsPlayerReference; |
12 | self.mediaSource = new MediaSource(); | 12 | self.mediaSource = new MediaSource(); |
13 | self.parser = new SegmentParser(); | 13 | self.parser = new SegmentParser(); |
14 | 14 | ||
15 | self.manifestController = null; | 15 | self.manifestController = null; |
16 | self.segmentController = null; | 16 | self.segmentController = null; |
17 | self.manifestLoaded = false; | 17 | self.manifestLoaded = false; |
18 | self.currentSegment = 0; | 18 | self.currentSegment = 0; |
19 | self.currentManifest = null; | 19 | self.currentManifest = null; |
20 | self.currentPlaylist = null; | 20 | self.currentPlaylist = null; |
21 | self.currentRendition = null; | 21 | self.currentRendition = null; |
22 | 22 | ||
23 | // Register Externall Callbacks | 23 | // Register Externall Callbacks |
24 | self.manifestLoadCompleteCallback; | 24 | self.manifestLoadCompleteCallback; |
25 | 25 | ||
26 | self.player.on('timeupdate', function () { | 26 | self.player.on('timeupdate', function () { |
27 | console.log(self.player.currentTime()); | 27 | console.log(self.player.currentTime()); |
28 | }); | 28 | }); |
29 | 29 | ||
30 | self.player.on('onsrcchange', function () { | 30 | self.player.on('onsrcchange', function () { |
31 | console.log('src change', self.player.currentSrc()); | 31 | console.log('src change', self.player.currentSrc()); |
32 | //if src.url.m3u8 -- loadManifest.url | 32 | //if src.url.m3u8 -- loadManifest.url |
33 | }); | 33 | }); |
34 | 34 | ||
35 | self.rendition = function (rendition) { | 35 | self.rendition = function (rendition) { |
36 | self.currentRendition = rendition; | 36 | self.currentRendition = rendition; |
37 | self.loadManifest(self.currentRendition.url, self.onM3U8LoadComplete, self.onM3U8LoadError, self.onM3U8Update); | 37 | self.loadManifest(self.currentRendition.url, self.onM3U8LoadComplete, self.onM3U8LoadError, self.onM3U8Update); |
38 | }; | 38 | }; |
39 | 39 | ||
40 | self.loadManifest = function (manifestUrl, onDataCallback, onErrorCallback, onUpdateCallback) { | 40 | self.loadManifest = function (manifestUrl, onDataCallback, onErrorCallback, onUpdateCallback) { |
41 | self.mediaSource.addEventListener('sourceopen', function (event) { | 41 | self.mediaSource.addEventListener('sourceopen', function (event) { |
42 | console.log('source open here'); | 42 | console.log('source open here'); |
43 | // feed parsed bytes into the player | 43 | // feed parsed bytes into the player |
44 | self.sourceBuffer = self.mediaSource.addSourceBuffer('video/flv; codecs="vp6,aac"'); | 44 | self.sourceBuffer = self.mediaSource.addSourceBuffer('video/flv; codecs="vp6,aac"'); |
45 | 45 | ||
46 | self.parser = new SegmentParser(); | 46 | self.parser = new SegmentParser(); |
47 | 47 | ||
48 | self.sourceBuffer.appendBuffer(self.parser.getFlvHeader(), video); | 48 | self.sourceBuffer.appendBuffer(self.parser.getFlvHeader(), video); |
49 | 49 | ||
50 | if( onDataCallback ) | 50 | if( onDataCallback ) |
51 | { | 51 | { |
52 | self.manifestLoadCompleteCallback = onDataCallback; | 52 | self.manifestLoadCompleteCallback = onDataCallback; |
53 | } | 53 | } |
54 | 54 | ||
55 | self.manifestController = new ManifestController(); | 55 | self.manifestController = new ManifestController(); |
56 | self.manifestController.loadManifest(manifestUrl, self.onM3U8LoadComplete, self.onM3U8LoadError, self.onM3U8Update); | 56 | self.manifestController.loadManifest(manifestUrl, self.onM3U8LoadComplete, self.onM3U8LoadError, self.onM3U8Update); |
57 | 57 | ||
58 | }, false); | 58 | }, false); |
59 | 59 | ||
60 | self.player.src({ | 60 | self.player.src({ |
61 | src: videojs.URL.createObjectURL(self.mediaSource), | 61 | src: videojs.URL.createObjectURL(self.mediaSource), |
62 | type: "video/flv" | 62 | type: "video/flv" |
63 | }); | 63 | }); |
64 | }; | 64 | }; |
65 | 65 | ||
66 | self.onM3U8LoadComplete = function (m3u8) { | 66 | self.onM3U8LoadComplete = function (m3u8) { |
67 | if (m3u8.invalidReasons.length == 0) { | 67 | if (m3u8.invalidReasons.length == 0) { |
68 | if(m3u8.isPlaylist) | 68 | if(m3u8.isPlaylist) |
69 | { | 69 | { |
70 | self.currentPlaylist = m3u8; | 70 | self.currentPlaylist = m3u8; |
71 | self.rendition(self.currentPlaylist.playlistItems[0]); | 71 | self.rendition(self.currentPlaylist.playlistItems[0]); |
72 | } else { | 72 | } else { |
73 | self.currentManifest = m3u8; | 73 | self.currentManifest = m3u8; |
74 | self.manifestLoaded = true; | 74 | self.manifestLoaded = true; |
75 | 75 | ||
76 | self.loadSegment(self.currentManifest.mediaItems[0]); | 76 | self.loadSegment(self.currentManifest.mediaItems[0]); |
77 | 77 | ||
78 | if(self.manifestLoadCompleteCallback) | 78 | if(self.manifestLoadCompleteCallback) |
79 | { | 79 | { |
80 | self.manifestLoadCompleteCallback(m3u8); | 80 | self.manifestLoadCompleteCallback(m3u8); |
81 | } | 81 | } |
82 | } | 82 | } |
83 | } | 83 | } |
84 | }; | 84 | }; |
85 | 85 | ||
86 | self.onM3U8LoadError = function (error) { | 86 | self.onM3U8LoadError = function (error) { |
87 | 87 | ||
88 | }; | 88 | }; |
89 | 89 | ||
90 | self.onM3U8Update = function (m3u8) { | 90 | self.onM3U8Update = function (m3u8) { |
91 | 91 | ||
92 | }; | 92 | }; |
93 | 93 | ||
94 | self.loadSegment = function(segment) { | 94 | self.loadSegment = function(segment) { |
95 | self.segmentController = new SegmentController(); | 95 | self.segmentController = new SegmentController(); |
96 | self.segmentController.loadSegment(segment.url, self.onSegmentLoadComplete, self.onSegmentLoadError); | 96 | self.segmentController.loadSegment(segment.url, self.onSegmentLoadComplete, self.onSegmentLoadError); |
97 | 97 | ||
98 | }; | 98 | }; |
99 | 99 | ||
100 | self.onSegmentLoadComplete = function (segment) { | 100 | self.onSegmentLoadComplete = function (segment) { |
101 | self.parser.parseSegmentBinaryData(segment.binaryData); | 101 | self.parser.parseSegmentBinaryData(segment.binaryData); |
102 | 102 | ||
103 | while (self.parser.tagsAvailable()) { | 103 | while (self.parser.tagsAvailable()) { |
104 | self.sourceBuffer.appendBuffer(self.parser.getNextTag().bytes, self.player); | 104 | self.sourceBuffer.appendBuffer(self.parser.getNextTag().bytes, self.player); |
105 | }; | 105 | }; |
106 | 106 | ||
107 | console.log('load another',self.currentSegment,self.currentManifest.mediaItems.length); | 107 | console.log('load another',self.currentSegment,self.currentManifest.mediaItems.length); |
108 | 108 | ||
109 | if(self.currentSegment < self.currentManifest.mediaItems.length-1) | 109 | if(self.currentSegment < self.currentManifest.mediaItems.length-1) |
110 | { | 110 | { |
111 | console.log('load another'); | 111 | console.log('load another'); |
112 | self.loadNextSegment(); | 112 | self.loadNextSegment(); |
113 | } | 113 | } |
114 | }; | 114 | }; |
115 | 115 | ||
116 | self.loadNextSegment = function () { | 116 | self.loadNextSegment = function () { |
117 | self.currentSegment++; | 117 | self.currentSegment++; |
118 | self.loadSegment(self.currentManifest.mediaItems[self.currentSegment]); | 118 | self.loadSegment(self.currentManifest.mediaItems[self.currentSegment]); |
119 | } | 119 | } |
120 | 120 | ||
121 | self.onSegmentLoadError = function (error) { | 121 | self.onSegmentLoadError = function (error) { |
122 | 122 | ||
123 | }; | 123 | }; |
124 | 124 | ||
125 | }; | ||
126 | })(this); | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
125 | }; | ||
126 | })(this); | ... | ... |
1 | (function(window) { | 1 | (function(window) { |
2 | var M3U8 = window.videojs.hls.M3U8; | 2 | var M3U8 = window.videojs.hls.M3U8; |
3 | |||
4 | window.videojs.hls.M3U8Parser = function() { | ||
5 | |||
6 | var self = this; | ||
7 | self.directory; | ||
8 | |||
9 | var tagTypes = window.videojs.hls.m3u8TagType; | ||
10 | var lines = []; | ||
11 | var data; | ||
12 | |||
13 | self.getTagType = function( lineData ) { | ||
14 | for ( var s in tagTypes ) | ||
15 | { | ||
16 | if (lineData.indexOf(tagTypes[s]) == 0) | ||
17 | { | ||
18 | return tagTypes[s]; | ||
19 | } | ||
20 | } | ||
21 | } | ||
22 | |||
23 | self.getTagValue = function ( lineData ) { | ||
24 | for ( var s in tagTypes ) | ||
25 | { | ||
26 | if (lineData.indexOf(tagTypes[s]) == 0) | ||
27 | { | ||
28 | return lineData.substr(tagTypes[s].length); | ||
29 | } | ||
30 | } | ||
31 | } | ||
32 | |||
33 | self.parse = function( rawDataString ) { | ||
34 | data = new M3U8(); | ||
35 | |||
36 | if(self.directory) | ||
37 | { | ||
38 | data.directory = self.directory; | ||
39 | } | ||
40 | |||
41 | if( rawDataString != undefined && rawDataString.toString().length > 0 ) | ||
42 | { | ||
43 | lines = rawDataString.split('\n'); | ||
44 | |||
45 | lines.forEach( | ||
46 | function(value,index) { | ||
47 | switch( self.getTagType(value) ) | ||
48 | { | ||
49 | case tagTypes.EXTM3U: | ||
50 | data.hasValidM3UTag = (index == 0); | ||
51 | if(!data.hasValidM3UTag) | ||
52 | { | ||
53 | data.invalidReasons.push("Invalid EXTM3U Tag"); | ||
54 | } | ||
55 | break; | ||
56 | |||
57 | case tagTypes.DISCONTINUITY: | ||
58 | break; | ||
59 | |||
60 | case tagTypes.PLAYLIST_TYPE: | ||
61 | if(self.getTagValue(value) == "VOD" || self.getTagValue(value) == "EVENT") | ||
62 | { | ||
63 | data.playlistType = self.getTagValue(value); | ||
64 | data.isPlaylist = true; | ||
65 | } else { | ||
66 | data.invalidReasons.push("Invalid Playlist Type Value"); | ||
67 | } | ||
68 | break; | ||
69 | |||
70 | case tagTypes.EXTINF: | ||
71 | var segment = {url: "unknown", byterange: -1, targetDuration: data.targetDuration }; | ||
72 | |||
73 | if( self.getTagType(lines[index+1]) == tagTypes.BYTERANGE ) | ||
74 | { | ||
75 | segment.byterange = self.getTagValue(lines[index+1]).split('@'); | ||
76 | segment.url = lines[index+2]; | ||
77 | } else | ||
78 | { | ||
79 | segment.url = lines[index+1]; | ||
80 | } | ||
81 | |||
82 | if(segment.url.indexOf("http")===-1 && self.directory) | ||
83 | { | ||
84 | if(data.directory[data.directory.length-1] === segment.url[0] && segment.url[0] === "/") | ||
85 | { | ||
86 | segment.url = segment.url.substr(1); | ||
87 | } | ||
88 | segment.url = self.directory + segment.url; | ||
89 | } | ||
90 | |||
91 | data.mediaItems.push(segment); | ||
92 | |||
93 | break; | ||
94 | |||
95 | case tagTypes.STREAM_INF: | ||
96 | var rendition = {}; | ||
97 | var attributes = value.substr(tagTypes.STREAM_INF.length).split(','); | ||
3 | 98 | ||
4 | window.videojs.hls.M3U8Parser = function() { | 99 | attributes.forEach(function(attr_value,attr_index) { |
100 | if(isNaN(attr_value.split('=')[1])){ | ||
101 | rendition[attr_value.split('=')[0].toLowerCase()] = attr_value.split('=')[1]; | ||
5 | 102 | ||
6 | var self = this; | 103 | if(rendition[attr_value.split('=')[0].toLowerCase()].split('x').length = 2) |
7 | self.directory; | 104 | { |
105 | rendition.resolution = { | ||
106 | width: Number(rendition[attr_value.split('=')[0].toLowerCase()].split('x')[0]), | ||
107 | height: Number(rendition[attr_value.split('=')[0].toLowerCase()].split('x')[1]) | ||
108 | } | ||
109 | } | ||
8 | 110 | ||
9 | var tagTypes = window.videojs.hls.m3u8TagType; | 111 | } else { |
10 | var lines = []; | 112 | rendition[attr_value.split('=')[0].toLowerCase()] = Number(attr_value.split('=')[1]); |
11 | var data; | 113 | } |
114 | }); | ||
12 | 115 | ||
13 | self.getTagType = function( lineData ) { | 116 | |
14 | for ( var s in tagTypes ) | 117 | if( self.getTagType(lines[index+1]) == tagTypes.BYTERANGE ) |
15 | { | 118 | { |
16 | if (lineData.indexOf(tagTypes[s]) == 0) | 119 | rendition.byterange = self.getTagValue(lines[index+1]).split('@'); |
17 | { | 120 | rendition.url = lines[index+2]; |
18 | return tagTypes[s]; | 121 | } else |
19 | } | 122 | { |
20 | } | 123 | rendition.url = lines[index+1]; |
21 | } | 124 | } |
22 | 125 | ||
23 | self.getTagValue = function ( lineData ) { | 126 | data.isPlaylist = true; |
24 | for ( var s in tagTypes ) | 127 | data.playlistItems.push(rendition); |
25 | { | 128 | break; |
26 | if (lineData.indexOf(tagTypes[s]) == 0) | 129 | |
27 | { | 130 | case tagTypes.TARGETDURATION: |
28 | return lineData.substr(tagTypes[s].length); | 131 | data.targetDuration = Number(self.getTagValue(value).split(',')[0]); |
29 | } | 132 | break; |
30 | } | 133 | |
134 | case tagTypes.ZEN_TOTAL_DURATION: | ||
135 | data.totalDuration = Number(self.getTagValue(value)); | ||
136 | break; | ||
137 | |||
138 | case tagTypes.VERSION: | ||
139 | data.version = Number(self.getTagValue(value)); | ||
140 | break; | ||
141 | |||
142 | case tagTypes.MEDIA_SEQUENCE: | ||
143 | data.mediaSequence = parseInt(self.getTagValue(value)); | ||
144 | break; | ||
145 | |||
146 | case tagTypes.ALLOW_CACHE: | ||
147 | if(self.getTagValue(value) == "YES" || self.getTagValue(value) == "NO") | ||
148 | { | ||
149 | data.allowCache = self.getTagValue(value); | ||
150 | } else { | ||
151 | data.invalidReasons.push("Invalid ALLOW_CACHE Value"); | ||
31 | } | 152 | } |
153 | break; | ||
154 | |||
155 | case tagTypes.ENDLIST: | ||
156 | data.hasEndTag = true; | ||
157 | break; | ||
158 | } | ||
159 | } | ||
160 | ) | ||
161 | } else { | ||
162 | data.invalidReasons.push("Empty Manifest"); | ||
163 | } | ||
164 | |||
165 | return data; | ||
32 | 166 | ||
33 | self.parse = function( rawDataString ) { | ||
34 | data = new M3U8(); | ||
35 | |||
36 | if(self.directory) | ||
37 | { | ||
38 | data.directory = self.directory; | ||
39 | } | ||
40 | |||
41 | if( rawDataString != undefined && rawDataString.toString().length > 0 ) | ||
42 | { | ||
43 | lines = rawDataString.split('\n'); | ||
44 | |||
45 | lines.forEach( | ||
46 | function(value,index) { | ||
47 | switch( self.getTagType(value) ) | ||
48 | { | ||
49 | case tagTypes.EXTM3U: | ||
50 | data.hasValidM3UTag = (index == 0); | ||
51 | if(!data.hasValidM3UTag) | ||
52 | { | ||
53 | data.invalidReasons.push("Invalid EXTM3U Tag"); | ||
54 | } | ||
55 | break; | ||
56 | |||
57 | case tagTypes.DISCONTINUITY: | ||
58 | break; | ||
59 | |||
60 | case tagTypes.PLAYLIST_TYPE: | ||
61 | if(self.getTagValue(value) == "VOD" || self.getTagValue(value) == "EVENT") | ||
62 | { | ||
63 | data.playlistType = self.getTagValue(value); | ||
64 | data.isPlaylist = true; | ||
65 | } else { | ||
66 | data.invalidReasons.push("Invalid Playlist Type Value"); | ||
67 | } | ||
68 | break; | ||
69 | |||
70 | case tagTypes.EXTINF: | ||
71 | var segment = {url: "unknown", byterange: -1, targetDuration: data.targetDuration }; | ||
72 | |||
73 | if( self.getTagType(lines[index+1]) == tagTypes.BYTERANGE ) | ||
74 | { | ||
75 | segment.byterange = self.getTagValue(lines[index+1]).split('@'); | ||
76 | segment.url = lines[index+2]; | ||
77 | } else | ||
78 | { | ||
79 | segment.url = lines[index+1]; | ||
80 | } | ||
81 | |||
82 | if(segment.url.indexOf("http")===-1 && self.directory) | ||
83 | { | ||
84 | if(data.directory[data.directory.length-1] === segment.url[0] && segment.url[0] === "/") | ||
85 | { | ||
86 | segment.url = segment.url.substr(1); | ||
87 | } | ||
88 | segment.url = self.directory + segment.url; | ||
89 | } | ||
90 | |||
91 | data.mediaItems.push(segment); | ||
92 | |||
93 | break; | ||
94 | |||
95 | case tagTypes.STREAM_INF: | ||
96 | var rendition = {}; | ||
97 | var attributes = value.substr(tagTypes.STREAM_INF.length).split(','); | ||
98 | |||
99 | attributes.forEach(function(attr_value,attr_index) { | ||
100 | if(isNaN(attr_value.split('=')[1])){ | ||
101 | rendition[attr_value.split('=')[0].toLowerCase()] = attr_value.split('=')[1]; | ||
102 | |||
103 | if(rendition[attr_value.split('=')[0].toLowerCase()].split('x').length = 2) | ||
104 | { | ||
105 | rendition.resolution = { | ||
106 | width: Number(rendition[attr_value.split('=')[0].toLowerCase()].split('x')[0]), | ||
107 | height: Number(rendition[attr_value.split('=')[0].toLowerCase()].split('x')[1]) | ||
108 | } | ||
109 | } | ||
110 | |||
111 | } else { | ||
112 | rendition[attr_value.split('=')[0].toLowerCase()] = Number(attr_value.split('=')[1]); | ||
113 | } | ||
114 | }); | ||
115 | |||
116 | |||
117 | if( self.getTagType(lines[index+1]) == tagTypes.BYTERANGE ) | ||
118 | { | ||
119 | rendition.byterange = self.getTagValue(lines[index+1]).split('@'); | ||
120 | rendition.url = lines[index+2]; | ||
121 | } else | ||
122 | { | ||
123 | rendition.url = lines[index+1]; | ||
124 | } | ||
125 | |||
126 | data.isPlaylist = true; | ||
127 | data.playlistItems.push(rendition); | ||
128 | break; | ||
129 | |||
130 | case tagTypes.TARGETDURATION: | ||
131 | data.targetDuration = Number(self.getTagValue(value).split(',')[0]); | ||
132 | break; | ||
133 | |||
134 | case tagTypes.ZEN_TOTAL_DURATION: | ||
135 | data.totalDuration = Number(self.getTagValue(value)); | ||
136 | break; | ||
137 | |||
138 | case tagTypes.VERSION: | ||
139 | data.version = Number(self.getTagValue(value)); | ||
140 | break; | ||
141 | |||
142 | case tagTypes.MEDIA_SEQUENCE: | ||
143 | data.mediaSequence = parseInt(self.getTagValue(value)); | ||
144 | break; | ||
145 | |||
146 | case tagTypes.ALLOW_CACHE: | ||
147 | if(self.getTagValue(value) == "YES" || self.getTagValue(value) == "NO") | ||
148 | { | ||
149 | data.allowCache = self.getTagValue(value); | ||
150 | } else { | ||
151 | data.invalidReasons.push("Invalid ALLOW_CACHE Value"); | ||
152 | } | ||
153 | break; | ||
154 | |||
155 | case tagTypes.ENDLIST: | ||
156 | data.hasEndTag = true; | ||
157 | break; | ||
158 | } | ||
159 | } | ||
160 | ) | ||
161 | } else { | ||
162 | data.invalidReasons.push("Empty Manifest"); | ||
163 | } | ||
164 | |||
165 | return data; | ||
166 | |||
167 | }; | ||
168 | }; | 167 | }; |
168 | }; | ||
169 | 169 | ||
170 | })(this); | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
170 | })(this); | ... | ... |
1 | window.videojs.hls.m3u8TagType = { | ||
2 | /* | ||
3 | * Derived from V8: http://tools.ietf.org/html/draft-pantos-http-live-streaming-08 | ||
4 | */ | ||
5 | |||
6 | /** | ||
7 | * Identifies manifest as Extended M3U - must be present on first line! | ||
8 | */ | ||
9 | EXTM3U:"#EXTM3U", | ||
10 | |||
11 | /** | ||
12 | * Specifies duration. | ||
13 | * Syntax: #EXTINF:<duration>,<title> | ||
14 | * Example: #EXTINF:10, | ||
15 | */ | ||
16 | EXTINF:"#EXTINF:", | ||
17 | |||
18 | /** | ||
19 | * Indicates that a media segment is a sub-range of the resource identified by its media URI. | ||
20 | * Syntax: #EXT-X-BYTERANGE:<n>[@o] | ||
21 | */ | ||
22 | BYTERANGE:"#EXT-X-BYTERANGE:", | ||
23 | |||
24 | /** | ||
25 | * Specifies the maximum media segment duration - applies to entire manifest. | ||
26 | * Syntax: #EXT-X-TARGETDURATION:<s> | ||
27 | * Example: #EXT-X-TARGETDURATION:10 | ||
28 | */ | ||
29 | TARGETDURATION:"#EXT-X-TARGETDURATION:", | ||
30 | |||
31 | /** | ||
32 | * Specifies the sequence number of the first URI in a manifest. | ||
33 | * Syntax: #EXT-X-MEDIA-SEQUENCE:<i> | ||
34 | * Example: #EXT-X-MEDIA-SEQUENCE:50 | ||
35 | */ | ||
36 | MEDIA_SEQUENCE:"#EXT-X-MEDIA-SEQUENCE:", | ||
37 | |||
38 | /** | ||
39 | * Specifies a method by which media segments can be decrypted, if encryption is present. | ||
40 | * Syntax: #EXT-X-KEY:<attribute-list> | ||
41 | * Note: This is likely irrelevant in the context of the Flash Player. | ||
42 | */ | ||
43 | KEY:"#EXT-X-KEY:", | ||
44 | |||
45 | /** | ||
46 | * Associates the first sample of a media segment with an absolute date and/or time. Applies only to the next media URI. | ||
47 | * Syntax: #EXT-X-PROGRAM-DATE-TIME:<YYYY-MM-DDThh:mm:ssZ> | ||
48 | * Example: #EXT-X-PROGRAM-DATE-TIME:2010-02-19T14:54:23.031+08:00 | ||
49 | */ | ||
50 | PROGRAM_DATE_TIME:"#EXT-X-PROGRAM-DATE-TIME:", | ||
51 | |||
52 | /** | ||
53 | * Indicates whether the client MAY or MUST NOT cache downloaded media segments for later replay. | ||
54 | * Syntax: #EXT-X-ALLOW-CACHE:<YES|NO> | ||
55 | * Note: This is likely irrelevant in the context of the Flash Player. | ||
56 | */ | ||
57 | ALLOW_CACHE:"#EXT-X-ALLOW_CACHE:", | ||
58 | |||
59 | /** | ||
60 | * Provides mutability information about the manifest. | ||
61 | * Syntax: #EXT-X-PLAYLIST-TYPE:<EVENT|VOD> | ||
62 | */ | ||
63 | PLAYLIST_TYPE:"#EXT-X-PLAYLIST-TYPE:", | ||
64 | |||
65 | /** | ||
66 | * Indicates that no more media segments will be added to the manifest. May occur ONCE, anywhere in the mainfest file. | ||
67 | */ | ||
68 | ENDLIST:"#EXT-X-ENDLIST", | ||
69 | |||
70 | /** | ||
71 | * Used to relate Playlists that contain alternative renditions of the same content. | ||
72 | * Syntax: #EXT-X-MEDIA:<attribute-list> | ||
73 | */ | ||
74 | MEDIA:"#EXT-X-MEDIA:", | ||
75 | |||
76 | /** | ||
77 | * Identifies a media URI as a Playlist file containing a multimedia presentation and provides information about that presentation. | ||
78 | * Syntax: #EXT-X-STREAM-INF:<attribute-list> | ||
79 | * <URI> | ||
80 | */ | ||
81 | STREAM_INF:"#EXT-X-STREAM-INF:", | ||
82 | |||
83 | /** | ||
84 | * Indicates an encoding discontinuity between the media segment that follows it and the one that preceded it. | ||
85 | */ | ||
86 | DISCONTINUITY:"#EXT-X-DISCONTINUITY", | ||
87 | |||
88 | /** | ||
89 | * Indicates that each media segment in the manifest describes a single I-frame. | ||
90 | */ | ||
91 | I_FRAMES_ONLY:"#EXT-X-I-FRAMES-ONLY", | ||
92 | |||
93 | /** | ||
94 | * Identifies a manifest file containing the I-frames of a multimedia presentation. It stands alone, in that it does not apply to a particular URI in the manifest. | ||
95 | * Syntax: #EXT-X-I-FRAME-STREAM-INF:<attribute-list> | ||
96 | */ | ||
97 | I_FRAME_STREAM_INF:"#EXT-X-I-FRAME-STREAM-INF:", | ||
98 | |||
99 | /** | ||
100 | * Indicates the compatibility version of the Playlist file. | ||
101 | * Syntax: #EXT-X-VERSION:<n> | ||
102 | */ | ||
103 | VERSION:"#EXT-X-VERSION:", | ||
104 | |||
105 | /** | ||
106 | * Indicates the total duration as reported by Zencoder. | ||
107 | * Syntax: #ZEN-TOTAL-DURATION:<n> | ||
108 | */ | ||
109 | ZEN_TOTAL_DURATION: "#ZEN-TOTAL-DURATION:" | ||
110 | |||
111 | }; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | (function(window) { | ||
2 | window.videojs.hls.m3u8TagType = { | ||
3 | /* | ||
4 | * Derived from V8: http://tools.ietf.org/html/draft-pantos-http-live-streaming-08 | ||
5 | */ | ||
6 | |||
7 | /** | ||
8 | * Identifies manifest as Extended M3U - must be present on first line! | ||
9 | */ | ||
10 | EXTM3U:"#EXTM3U", | ||
11 | |||
12 | /** | ||
13 | * Specifies duration. | ||
14 | * Syntax: #EXTINF:<duration>,<title> | ||
15 | * Example: #EXTINF:10, | ||
16 | */ | ||
17 | EXTINF:"#EXTINF:", | ||
18 | |||
19 | /** | ||
20 | * Indicates that a media segment is a sub-range of the resource identified by its media URI. | ||
21 | * Syntax: #EXT-X-BYTERANGE:<n>[@o] | ||
22 | */ | ||
23 | BYTERANGE:"#EXT-X-BYTERANGE:", | ||
24 | |||
25 | /** | ||
26 | * Specifies the maximum media segment duration - applies to entire manifest. | ||
27 | * Syntax: #EXT-X-TARGETDURATION:<s> | ||
28 | * Example: #EXT-X-TARGETDURATION:10 | ||
29 | */ | ||
30 | TARGETDURATION:"#EXT-X-TARGETDURATION:", | ||
31 | |||
32 | /** | ||
33 | * Specifies the sequence number of the first URI in a manifest. | ||
34 | * Syntax: #EXT-X-MEDIA-SEQUENCE:<i> | ||
35 | * Example: #EXT-X-MEDIA-SEQUENCE:50 | ||
36 | */ | ||
37 | MEDIA_SEQUENCE:"#EXT-X-MEDIA-SEQUENCE:", | ||
38 | |||
39 | /** | ||
40 | * Specifies a method by which media segments can be decrypted, if encryption is present. | ||
41 | * Syntax: #EXT-X-KEY:<attribute-list> | ||
42 | * Note: This is likely irrelevant in the context of the Flash Player. | ||
43 | */ | ||
44 | KEY:"#EXT-X-KEY:", | ||
45 | |||
46 | /** | ||
47 | * Associates the first sample of a media segment with an absolute date and/or time. Applies only to the next media URI. | ||
48 | * Syntax: #EXT-X-PROGRAM-DATE-TIME:<YYYY-MM-DDThh:mm:ssZ> | ||
49 | * Example: #EXT-X-PROGRAM-DATE-TIME:2010-02-19T14:54:23.031+08:00 | ||
50 | */ | ||
51 | PROGRAM_DATE_TIME:"#EXT-X-PROGRAM-DATE-TIME:", | ||
52 | |||
53 | /** | ||
54 | * Indicates whether the client MAY or MUST NOT cache downloaded media segments for later replay. | ||
55 | * Syntax: #EXT-X-ALLOW-CACHE:<YES|NO> | ||
56 | * Note: This is likely irrelevant in the context of the Flash Player. | ||
57 | */ | ||
58 | ALLOW_CACHE:"#EXT-X-ALLOW_CACHE:", | ||
59 | |||
60 | /** | ||
61 | * Provides mutability information about the manifest. | ||
62 | * Syntax: #EXT-X-PLAYLIST-TYPE:<EVENT|VOD> | ||
63 | */ | ||
64 | PLAYLIST_TYPE:"#EXT-X-PLAYLIST-TYPE:", | ||
65 | |||
66 | /** | ||
67 | * Indicates that no more media segments will be added to the manifest. May occur ONCE, anywhere in the mainfest file. | ||
68 | */ | ||
69 | ENDLIST:"#EXT-X-ENDLIST", | ||
70 | |||
71 | /** | ||
72 | * Used to relate Playlists that contain alternative renditions of the same content. | ||
73 | * Syntax: #EXT-X-MEDIA:<attribute-list> | ||
74 | */ | ||
75 | MEDIA:"#EXT-X-MEDIA:", | ||
76 | |||
77 | /** | ||
78 | * Identifies a media URI as a Playlist file containing a multimedia presentation and provides information about that presentation. | ||
79 | * Syntax: #EXT-X-STREAM-INF:<attribute-list> | ||
80 | * <URI> | ||
81 | */ | ||
82 | STREAM_INF:"#EXT-X-STREAM-INF:", | ||
83 | |||
84 | /** | ||
85 | * Indicates an encoding discontinuity between the media segment that follows it and the one that preceded it. | ||
86 | */ | ||
87 | DISCONTINUITY:"#EXT-X-DISCONTINUITY", | ||
88 | |||
89 | /** | ||
90 | * Indicates that each media segment in the manifest describes a single I-frame. | ||
91 | */ | ||
92 | I_FRAMES_ONLY:"#EXT-X-I-FRAMES-ONLY", | ||
93 | |||
94 | /** | ||
95 | * Identifies a manifest file containing the I-frames of a multimedia presentation. It stands alone, in that it does not apply to a particular URI in the manifest. | ||
96 | * Syntax: #EXT-X-I-FRAME-STREAM-INF:<attribute-list> | ||
97 | */ | ||
98 | I_FRAME_STREAM_INF:"#EXT-X-I-FRAME-STREAM-INF:", | ||
99 | |||
100 | /** | ||
101 | * Indicates the compatibility version of the Playlist file. | ||
102 | * Syntax: #EXT-X-VERSION:<n> | ||
103 | */ | ||
104 | VERSION:"#EXT-X-VERSION:", | ||
105 | |||
106 | /** | ||
107 | * Indicates the total duration as reported by Zencoder. | ||
108 | * Syntax: #ZEN-TOTAL-DURATION:<n> | ||
109 | */ | ||
110 | ZEN_TOTAL_DURATION: "#ZEN-TOTAL-DURATION:" | ||
111 | |||
112 | }; | ||
113 | })(this); | ... | ... |
1 | (function(window) { | 1 | (function(window) { |
2 | window.videojs.hls.M3U8 = function() { | ||
3 | this.directory = ""; | ||
4 | this.allowCache = "NO"; | ||
5 | this.playlistItems = []; | ||
6 | this.mediaItems = []; | ||
7 | this.iFrameItems = []; | ||
8 | this.invalidReasons = []; | ||
9 | this.hasValidM3UTag = false; | ||
10 | this.hasEndTag = false; | ||
11 | this.targetDuration = -1; | ||
12 | this.totalDuration = -1; | ||
13 | this.isPlaylist = false; | ||
14 | this.playlistType = ""; | ||
15 | this.mediaSequence = -1; | ||
16 | this.version = -1; | ||
17 | } | ||
18 | })(this); | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
2 | window.videojs.hls.M3U8 = function() { | ||
3 | this.directory = ""; | ||
4 | this.allowCache = "NO"; | ||
5 | this.playlistItems = []; | ||
6 | this.mediaItems = []; | ||
7 | this.iFrameItems = []; | ||
8 | this.invalidReasons = []; | ||
9 | this.hasValidM3UTag = false; | ||
10 | this.hasEndTag = false; | ||
11 | this.targetDuration = -1; | ||
12 | this.totalDuration = -1; | ||
13 | this.isPlaylist = false; | ||
14 | this.playlistType = ""; | ||
15 | this.mediaSequence = -1; | ||
16 | this.version = -1; | ||
17 | } | ||
18 | })(this); | ... | ... |
1 | (function (window) { | 1 | (function (window) { |
2 | var M3U8 = window.videojs.hls.M3U8; | 2 | var M3U8 = window.videojs.hls.M3U8; |
3 | var M3U8Parser = window.videojs.hls.M3U8Parser; | 3 | var M3U8Parser = window.videojs.hls.M3U8Parser; |
4 | 4 | ||
5 | window.videojs.hls.ManifestController = function () { | 5 | window.videojs.hls.ManifestController = function () { |
6 | var self = this; | 6 | var self = this; |
7 | 7 | ||
8 | self.parser; | 8 | self.parser; |
9 | self.data; | 9 | self.data; |
10 | self.url; | 10 | self.url; |
11 | 11 | ||
12 | self.onDataCallback; | 12 | self.onDataCallback; |
13 | self.onErrorCallback; | 13 | self.onErrorCallback; |
14 | self.onUpdateCallback; | 14 | self.onUpdateCallback; |
15 | 15 | ||
16 | self.loadManifest = function (manifestUrl, onDataCallback, onErrorCallback, onUpdateCallback) { | 16 | self.loadManifest = function (manifestUrl, onDataCallback, onErrorCallback, onUpdateCallback) { |
17 | self.url = manifestUrl; | 17 | self.url = manifestUrl; |
18 | 18 | ||
19 | if (onDataCallback) { | 19 | if (onDataCallback) { |
20 | self.onDataCallback = onDataCallback; | 20 | self.onDataCallback = onDataCallback; |
21 | } | 21 | } |
22 | if (onErrorCallback) { | 22 | if (onErrorCallback) { |
23 | self.onErrorCallback = onErrorCallback; | 23 | self.onErrorCallback = onErrorCallback; |
24 | } | 24 | } |
25 | 25 | ||
26 | if (onUpdateCallback) { | 26 | if (onUpdateCallback) { |
27 | self.onUpdateCallback = onUpdateCallback; | 27 | self.onUpdateCallback = onUpdateCallback; |
28 | } | 28 | } |
29 | 29 | ||
30 | vjs.get(manifestUrl, self.onManifestLoadComplete, self.onManifestLoadError); | 30 | vjs.get(manifestUrl, self.onManifestLoadComplete, self.onManifestLoadError); |
31 | }; | 31 | }; |
32 | 32 | ||
33 | self.parseManifest = function (dataAsString) { | 33 | self.parseManifest = function (dataAsString) { |
34 | self.parser = new M3U8Parser(); | 34 | self.parser = new M3U8Parser(); |
35 | self.parser.directory = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/.exec(self.url).slice(1)[1]; | 35 | self.parser.directory = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/.exec(self.url).slice(1)[1]; |
36 | self.data = self.parser.parse(dataAsString); | 36 | self.data = self.parser.parse(dataAsString); |
37 | 37 | ||
38 | return self.data; | 38 | return self.data; |
39 | }; | 39 | }; |
40 | 40 | ||
41 | self.onManifestLoadComplete = function (response) { | 41 | self.onManifestLoadComplete = function (response) { |
42 | var output = self.parseManifest(response); | 42 | var output = self.parseManifest(response); |
43 | 43 | ||
44 | if (self.onDataCallback != undefined) { | 44 | if (self.onDataCallback != undefined) { |
45 | self.onDataCallback(output); | 45 | self.onDataCallback(output); |
46 | } | 46 | } |
47 | }; | 47 | }; |
48 | 48 | ||
49 | self.onManifestLoadError = function (err) { | 49 | self.onManifestLoadError = function (err) { |
50 | if (self.onErrorCallback != undefined) { | 50 | if (self.onErrorCallback != undefined) { |
51 | self.onErrorCallback((err != undefined) ? err : null); | 51 | self.onErrorCallback((err != undefined) ? err : null); |
52 | } | 52 | } |
53 | }; | 53 | }; |
54 | } | 54 | } |
55 | })(this); | 55 | })(this); | ... | ... |
1 | (function(window) { | 1 | (function(window) { |
2 | 2 | ||
3 | window.videojs.hls.SegmentController = function(){ | 3 | window.videojs.hls.SegmentController = function(){ |
4 | 4 | ||
5 | var self = this; | 5 | var self = this; |
6 | 6 | ||
7 | self.url; | 7 | self.url; |
8 | 8 | ||
9 | self.requestTimestamp; | 9 | self.requestTimestamp; |
10 | self.responseTimestamp; | 10 | self.responseTimestamp; |
11 | self.data; | 11 | self.data; |
12 | 12 | ||
13 | self.onDataCallback; | 13 | self.onDataCallback; |
14 | self.onErrorCallback; | 14 | self.onErrorCallback; |
15 | self.onUpdateCallback; | 15 | self.onUpdateCallback; |
16 | 16 | ||
17 | self.loadSegment = function ( segmentUrl, onDataCallback, onErrorCallback, onUpdateCallback ) { | 17 | self.loadSegment = function ( segmentUrl, onDataCallback, onErrorCallback, onUpdateCallback ) { |
18 | self.url = segmentUrl; | 18 | self.url = segmentUrl; |
19 | self.onDataCallback = onDataCallback; | 19 | self.onDataCallback = onDataCallback; |
20 | self.onErrorCallback = onErrorCallback; | 20 | self.onErrorCallback = onErrorCallback; |
21 | self.onUpdateCallback = onUpdateCallback; | 21 | self.onUpdateCallback = onUpdateCallback; |
22 | self.requestTimestamp = new Date().getTime(); | 22 | self.requestTimestamp = new Date().getTime(); |
23 | 23 | ||
24 | var req = new XMLHttpRequest(); | 24 | var req = new XMLHttpRequest(); |
25 | req.open('GET', segmentUrl, true); | 25 | req.open('GET', segmentUrl, true); |
26 | req.responseType = 'arraybuffer'; | 26 | req.responseType = 'arraybuffer'; |
27 | req.onload = function(response) { | 27 | req.onload = function(response) { |
28 | self.onSegmentLoadComplete(new Uint8Array(req.response)); | 28 | self.onSegmentLoadComplete(new Uint8Array(req.response)); |
29 | }; | 29 | }; |
30 | 30 | ||
31 | req.send(null); | 31 | req.send(null); |
32 | 32 | ||
33 | //vjs.get(segmentUrl, self.onSegmentLoadComplete, self.onSegmentLoadError); | 33 | //vjs.get(segmentUrl, self.onSegmentLoadComplete, self.onSegmentLoadError); |
34 | }; | 34 | }; |
35 | 35 | ||
36 | self.parseSegment = function ( incomingData ) { | 36 | self.parseSegment = function ( incomingData ) { |
37 | // Add David's code later // | 37 | // Add David's code later // |
38 | console.log('got segment data', incomingData.byteLength); | 38 | console.log('got segment data', incomingData.byteLength); |
39 | 39 | ||
40 | self.data = {}; | 40 | self.data = {}; |
41 | self.data.binaryData = incomingData; | 41 | self.data.binaryData = incomingData; |
42 | self.data.url = self.url; | 42 | self.data.url = self.url; |
43 | self.data.isCached = false; | 43 | self.data.isCached = false; |
44 | self.data.requestTimestamp = self.requestTimestamp; | 44 | self.data.requestTimestamp = self.requestTimestamp; |
45 | self.data.responseTimestamp = self.responseTimestamp; | 45 | self.data.responseTimestamp = self.responseTimestamp; |
46 | self.data.byteLength = incomingData.byteLength; | 46 | self.data.byteLength = incomingData.byteLength; |
47 | self.data.isCached = ( parseInt(self.responseTimestamp - self.requestTimestamp) < 75 ); | 47 | self.data.isCached = ( parseInt(self.responseTimestamp - self.requestTimestamp) < 75 ); |
48 | self.data.throughput = self.calculateThroughput(self.data.byteLength, self.requestTimestamp ,self.responseTimestamp); | 48 | self.data.throughput = self.calculateThroughput(self.data.byteLength, self.requestTimestamp ,self.responseTimestamp); |
49 | 49 | ||
50 | return self.data; | 50 | return self.data; |
51 | }; | 51 | }; |
52 | 52 | ||
53 | self.calculateThroughput = function(dataAmount, startTime, endTime) { | 53 | self.calculateThroughput = function(dataAmount, startTime, endTime) { |
54 | return Math.round(dataAmount/(endTime-startTime)*1000)*8; | 54 | return Math.round(dataAmount/(endTime-startTime)*1000)*8; |
55 | } | 55 | } |
56 | 56 | ||
57 | self.onSegmentLoadComplete = function(response) { | 57 | self.onSegmentLoadComplete = function(response) { |
58 | self.responseTimestamp = new Date().getTime(); | 58 | self.responseTimestamp = new Date().getTime(); |
59 | 59 | ||
60 | var output = self.parseSegment(response); | 60 | var output = self.parseSegment(response); |
61 | 61 | ||
62 | if(self.onDataCallback != undefined) | 62 | if(self.onDataCallback != undefined) |
63 | { | 63 | { |
64 | self.onDataCallback(output); | 64 | self.onDataCallback(output); |
65 | } | 65 | } |
66 | }; | 66 | }; |
67 | 67 | ||
68 | self.onSegmentLoadError = function(err) { | 68 | self.onSegmentLoadError = function(err) { |
69 | if(err) | 69 | if(err) |
70 | { | 70 | { |
71 | console.log(err.message); | 71 | console.log(err.message); |
72 | } | 72 | } |
73 | 73 | ||
74 | if(self.onErrorCallback != undefined) | 74 | if(self.onErrorCallback != undefined) |
75 | { | 75 | { |
76 | onErrorCallback((err != undefined) ? err : null); | 76 | onErrorCallback((err != undefined) ? err : null); |
77 | } | 77 | } |
78 | }; | 78 | }; |
79 | } | 79 | } |
80 | })(this); | 80 | })(this); | ... | ... |
1 | window.playlistData = '#EXTM3U\n'+ | 1 | window.playlistData = '#EXTM3U\n'+ |
2 | '#EXT-X-TARGETDURATION:10\n' + | ||
3 | '#EXT-X-VERSION:4\n' + | ||
4 | '#EXT-X-MEDIA-SEQUENCE:0\n' + | ||
5 | '#EXT-X-PLAYLIST-TYPE:VOD\n' + | ||
6 | '#EXTINF:10,\n' + | ||
7 | '#EXT-X-BYTERANGE:522828@0\n' + | ||
8 | 'hls_450k_video.ts\n' + | ||
9 | '#EXTINF:10,\n' + | ||
10 | '#EXT-X-BYTERANGE:587500@522828\n' + | ||
11 | 'hls_450k_video.ts\n' + | ||
12 | '#EXTINF:10,\n' + | ||
13 | '#EXT-X-BYTERANGE:713084@1110328\n' + | ||
14 | 'hls_450k_video.ts\n' + | ||
15 | '#EXTINF:10,\n' + | ||
16 | '#EXT-X-BYTERANGE:476580@1823412\n' + | ||
17 | 'hls_450k_video.ts\n' + | ||
18 | '#EXTINF:10,\n' + | ||
19 | '#EXT-X-BYTERANGE:535612@2299992\n' + | ||
20 | 'hls_450k_video.ts\n' + | ||
21 | '#EXTINF:10,\n' + | ||
22 | '#EXT-X-BYTERANGE:207176@2835604\n' + | ||
23 | 'hls_450k_video.ts\n' + | ||
24 | '#EXTINF:10,\n' + | ||
25 | '#EXT-X-BYTERANGE:455900@3042780\n' + | ||
26 | 'hls_450k_video.ts\n' + | ||
27 | '#EXTINF:10,\n' + | ||
28 | '#EXT-X-BYTERANGE:657248@3498680\n' + | ||
29 | 'hls_450k_video.ts\n' + | ||
30 | '#EXTINF:10,\n' + | ||
31 | '#EXT-X-BYTERANGE:571708@4155928\n' + | ||
32 | 'hls_450k_video.ts\n' + | ||
33 | '#EXTINF:10,\n' + | ||
34 | '#EXT-X-BYTERANGE:485040@4727636\n' + | ||
35 | 'hls_450k_video.ts\n' + | ||
36 | '#EXTINF:10,\n' + | ||
37 | '#EXT-X-BYTERANGE:709136@5212676\n' + | ||
38 | 'hls_450k_video.ts\n' + | ||
39 | '#EXTINF:10,\n' + | ||
40 | '#EXT-X-BYTERANGE:730004@5921812\n' + | ||
41 | 'hls_450k_video.ts\n' + | ||
42 | '#EXTINF:10,\n' + | ||
43 | '#EXT-X-BYTERANGE:456276@6651816\n' + | ||
44 | 'hls_450k_video.ts\n' + | ||
45 | '#EXTINF:10,\n' + | ||
46 | '#EXT-X-BYTERANGE:468684@7108092\n' + | ||
47 | 'hls_450k_video.ts' + | ||
48 | '#EXTINF:10,\n' + | ||
49 | '#EXT-X-BYTERANGE:444996@7576776\n' + | ||
50 | 'hls_450k_video.ts\n' + | ||
51 | '#EXTINF:10,\n' + | ||
52 | '#EXT-X-BYTERANGE:331444@8021772\n' + | ||
53 | 'hls_450k_video.ts\n' + | ||
54 | '#EXTINF:1.4167,\n' + | ||
55 | '#EXT-X-BYTERANGE:44556@8353216\n' + | ||
56 | 'hls_450k_video.ts\n' + | ||
57 | '#EXT-X-ENDLIST'; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
2 | '#EXT-X-TARGETDURATION:10\n' + | ||
3 | '#EXT-X-VERSION:4\n' + | ||
4 | '#EXT-X-MEDIA-SEQUENCE:0\n' + | ||
5 | '#EXT-X-PLAYLIST-TYPE:VOD\n' + | ||
6 | '#EXTINF:10,\n' + | ||
7 | '#EXT-X-BYTERANGE:522828@0\n' + | ||
8 | 'hls_450k_video.ts\n' + | ||
9 | '#EXTINF:10,\n' + | ||
10 | '#EXT-X-BYTERANGE:587500@522828\n' + | ||
11 | 'hls_450k_video.ts\n' + | ||
12 | '#EXTINF:10,\n' + | ||
13 | '#EXT-X-BYTERANGE:713084@1110328\n' + | ||
14 | 'hls_450k_video.ts\n' + | ||
15 | '#EXTINF:10,\n' + | ||
16 | '#EXT-X-BYTERANGE:476580@1823412\n' + | ||
17 | 'hls_450k_video.ts\n' + | ||
18 | '#EXTINF:10,\n' + | ||
19 | '#EXT-X-BYTERANGE:535612@2299992\n' + | ||
20 | 'hls_450k_video.ts\n' + | ||
21 | '#EXTINF:10,\n' + | ||
22 | '#EXT-X-BYTERANGE:207176@2835604\n' + | ||
23 | 'hls_450k_video.ts\n' + | ||
24 | '#EXTINF:10,\n' + | ||
25 | '#EXT-X-BYTERANGE:455900@3042780\n' + | ||
26 | 'hls_450k_video.ts\n' + | ||
27 | '#EXTINF:10,\n' + | ||
28 | '#EXT-X-BYTERANGE:657248@3498680\n' + | ||
29 | 'hls_450k_video.ts\n' + | ||
30 | '#EXTINF:10,\n' + | ||
31 | '#EXT-X-BYTERANGE:571708@4155928\n' + | ||
32 | 'hls_450k_video.ts\n' + | ||
33 | '#EXTINF:10,\n' + | ||
34 | '#EXT-X-BYTERANGE:485040@4727636\n' + | ||
35 | 'hls_450k_video.ts\n' + | ||
36 | '#EXTINF:10,\n' + | ||
37 | '#EXT-X-BYTERANGE:709136@5212676\n' + | ||
38 | 'hls_450k_video.ts\n' + | ||
39 | '#EXTINF:10,\n' + | ||
40 | '#EXT-X-BYTERANGE:730004@5921812\n' + | ||
41 | 'hls_450k_video.ts\n' + | ||
42 | '#EXTINF:10,\n' + | ||
43 | '#EXT-X-BYTERANGE:456276@6651816\n' + | ||
44 | 'hls_450k_video.ts\n' + | ||
45 | '#EXTINF:10,\n' + | ||
46 | '#EXT-X-BYTERANGE:468684@7108092\n' + | ||
47 | 'hls_450k_video.ts' + | ||
48 | '#EXTINF:10,\n' + | ||
49 | '#EXT-X-BYTERANGE:444996@7576776\n' + | ||
50 | 'hls_450k_video.ts\n' + | ||
51 | '#EXTINF:10,\n' + | ||
52 | '#EXT-X-BYTERANGE:331444@8021772\n' + | ||
53 | 'hls_450k_video.ts\n' + | ||
54 | '#EXTINF:1.4167,\n' + | ||
55 | '#EXT-X-BYTERANGE:44556@8353216\n' + | ||
56 | 'hls_450k_video.ts\n' + | ||
57 | '#EXT-X-ENDLIST'; | ... | ... |
This diff is collapsed.
Click to expand it.
-
Please register or sign in to post a comment