Merge pull request #1 from tojohnson/master
Tom's Hack week progress
Showing
11 changed files
with
706 additions
and
6 deletions
src/m3u8/m3u8-parser.js
0 → 100644
1 | (function(window) { | ||
2 | var M3U8 = window.videojs.hls.M3U8; | ||
3 | |||
4 | window.videojs.hls.M3U8Parser = function() { | ||
5 | |||
6 | var self = this; | ||
7 | var tagTypes = window.videojs.hls.m3u8TagType; | ||
8 | var lines = []; | ||
9 | var data; | ||
10 | |||
11 | self.getTagType = function( lineData ) { | ||
12 | for ( var s in tagTypes ) | ||
13 | { | ||
14 | if (lineData.indexOf(tagTypes[s]) == 0) | ||
15 | { | ||
16 | return tagTypes[s]; | ||
17 | } | ||
18 | } | ||
19 | } | ||
20 | |||
21 | self.getTagValue = function ( lineData ) { | ||
22 | for ( var s in tagTypes ) | ||
23 | { | ||
24 | if (lineData.indexOf(tagTypes[s]) == 0) | ||
25 | { | ||
26 | return lineData.substr(tagTypes[s].length); | ||
27 | } | ||
28 | } | ||
29 | } | ||
30 | |||
31 | self.parse = function( rawDataString ) { | ||
32 | data = new M3U8(); | ||
33 | |||
34 | if( rawDataString != undefined && rawDataString.toString().length > 0 ) | ||
35 | { | ||
36 | lines = rawDataString.split('\n'); | ||
37 | |||
38 | lines.forEach( | ||
39 | function(value,index) { | ||
40 | switch( self.getTagType(value) ) | ||
41 | { | ||
42 | case tagTypes.EXTM3U: | ||
43 | data.hasValidM3UTag = (index == 0); | ||
44 | if(!data.hasValidM3UTag) | ||
45 | { | ||
46 | data.invalidReasons.push("Invalid EXTM3U Tag"); | ||
47 | } | ||
48 | break; | ||
49 | |||
50 | case tagTypes.DISCONTINUITY: | ||
51 | break; | ||
52 | |||
53 | case tagTypes.PLAYLIST_TYPE: | ||
54 | if(self.getTagValue(value) == "VOD" || self.getTagValue(value) == "EVENT") | ||
55 | { | ||
56 | data.playlistType = self.getTagValue(value); | ||
57 | data.isPlaylist = true; | ||
58 | } else { | ||
59 | data.invalidReasons.push("Invalid Playlist Type Value"); | ||
60 | } | ||
61 | break; | ||
62 | |||
63 | case tagTypes.EXTINF: | ||
64 | var segment = {url: "unknown", byterange: -1, targetDuration: data.targetDuration }; | ||
65 | |||
66 | if( self.getTagType(lines[index+1]) == tagTypes.BYTERANGE ) | ||
67 | { | ||
68 | segment.byterange = self.getTagValue(lines[index+1]).split('@'); | ||
69 | segment.url = lines[index+2]; | ||
70 | } else | ||
71 | { | ||
72 | segment.url = lines[index+1]; | ||
73 | } | ||
74 | |||
75 | data.mediaItems.push(segment); | ||
76 | |||
77 | break; | ||
78 | |||
79 | case tagTypes.STREAM_INF: | ||
80 | var rendition = {}; | ||
81 | var attributes = value.substr(tagTypes.STREAM_INF.length).split(','); | ||
82 | |||
83 | attributes.forEach(function(attr_value,attr_index) { | ||
84 | if(isNaN(attr_value.split('=')[1])){ | ||
85 | rendition[attr_value.split('=')[0].toLowerCase()] = attr_value.split('=')[1]; | ||
86 | |||
87 | if(rendition[attr_value.split('=')[0].toLowerCase()].split('x').length = 2) | ||
88 | { | ||
89 | rendition.resolution = { | ||
90 | width: Number(rendition[attr_value.split('=')[0].toLowerCase()].split('x')[0]), | ||
91 | height: Number(rendition[attr_value.split('=')[0].toLowerCase()].split('x')[1]) | ||
92 | } | ||
93 | } | ||
94 | |||
95 | } else { | ||
96 | rendition[attr_value.split('=')[0].toLowerCase()] = Number(attr_value.split('=')[1]); | ||
97 | } | ||
98 | }); | ||
99 | |||
100 | |||
101 | if( self.getTagType(lines[index+1]) == tagTypes.BYTERANGE ) | ||
102 | { | ||
103 | rendition.byterange = self.getTagValue(lines[index+1]).split('@'); | ||
104 | rendition.url = lines[index+2]; | ||
105 | } else | ||
106 | { | ||
107 | rendition.url = lines[index+1]; | ||
108 | } | ||
109 | |||
110 | data.isPlaylist = true; | ||
111 | data.playlistItems.push(rendition); | ||
112 | break; | ||
113 | |||
114 | case tagTypes.TARGETDURATION: | ||
115 | data.targetDuration = Number(self.getTagValue(value).split(',')[0]); | ||
116 | break; | ||
117 | |||
118 | case tagTypes.ZEN_TOTAL_DURATION: | ||
119 | data.totalDuration = self.getTagValue(value); | ||
120 | break; | ||
121 | |||
122 | case tagTypes.VERSION: | ||
123 | data.version = Number(self.getTagValue(value)); | ||
124 | break; | ||
125 | |||
126 | case tagTypes.MEDIA_SEQUENCE: | ||
127 | data.mediaSequence = parseInt(self.getTagValue(value)); | ||
128 | break; | ||
129 | |||
130 | case tagTypes.ALLOW_CACHE: | ||
131 | if(self.getTagValue(value) == "YES" || self.getTagValue(value) == "NO") | ||
132 | { | ||
133 | data.allowCache = self.getTagValue(value); | ||
134 | } else { | ||
135 | data.invalidReasons.push("Invalid ALLOW_CACHE Value"); | ||
136 | } | ||
137 | break; | ||
138 | |||
139 | case tagTypes.ENDLIST: | ||
140 | data.hasEndTag = true; | ||
141 | break; | ||
142 | } | ||
143 | } | ||
144 | ) | ||
145 | } else { | ||
146 | data.invalidReasons.push("Empty Manifest"); | ||
147 | } | ||
148 | |||
149 | return data; | ||
150 | |||
151 | }; | ||
152 | }; | ||
153 | |||
154 | })(this); | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
src/m3u8/m3u8-tag-types.js
0 → 100644
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 |
src/m3u8/m3u8.js
0 → 100644
1 | (function(window) { | ||
2 | window.videojs.hls.M3U8 = function() { | ||
3 | this.allowCache = "NO"; | ||
4 | this.playlistItems = []; | ||
5 | this.mediaItems = []; | ||
6 | this.iFrameItems = []; | ||
7 | this.invalidReasons = []; | ||
8 | this.hasValidM3UTag = false; | ||
9 | this.hasEndTag = false; | ||
10 | this.targetDuration = -1; | ||
11 | this.totalDuration = -1; | ||
12 | this.isPlaylist = false; | ||
13 | this.playlistType = ""; | ||
14 | this.mediaSequence = -1; | ||
15 | this.version = -1; | ||
16 | } | ||
17 | })(this); | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
src/manifest-controller.js
0 → 100644
1 | (function(window) { | ||
2 | var M3U8 = window.videojs.hls.M3U8; | ||
3 | var M3U8Parser = window.videojs.hls.M3U8Parser; | ||
4 | |||
5 | window.videojs.hls.ManifestController = function(){ | ||
6 | |||
7 | var self = this; | ||
8 | var parser; | ||
9 | var data; | ||
10 | |||
11 | var onDataCallback; | ||
12 | var onErrorCallback; | ||
13 | var onUpdateCallback; | ||
14 | |||
15 | self.loadManifest = function ( manifestUrl, onDataCallback, onErrorCallback, onUpdateCallback ) { | ||
16 | self.onDataCallback = onDataCallback; | ||
17 | self.onErrorCallback = onErrorCallback; | ||
18 | self.onUpdateCallback = onUpdateCallback; | ||
19 | |||
20 | vjs.get(manifestUrl, self.onManifestLoadComplete, self.onManifestLoadError); | ||
21 | }; | ||
22 | |||
23 | self.parseManifest = function ( dataAsString ) { | ||
24 | self.parser = new M3U8Parser(); | ||
25 | self.data = self.parser.parse( dataAsString ); | ||
26 | |||
27 | return self.data; | ||
28 | }; | ||
29 | |||
30 | self.onManifestLoadComplete = function(response) { | ||
31 | var output = self.parseManifest(response); | ||
32 | |||
33 | if(self.onDataCallback != undefined) | ||
34 | { | ||
35 | self.onDataCallback(output); | ||
36 | } | ||
37 | }; | ||
38 | |||
39 | self.onManifestLoadError = function(err) { | ||
40 | if(err) | ||
41 | { | ||
42 | console.log(err.message); | ||
43 | } | ||
44 | |||
45 | if(self.onErrorCallback != undefined) | ||
46 | { | ||
47 | onErrorCallback((err != undefined) ? err : null); | ||
48 | } | ||
49 | }; | ||
50 | } | ||
51 | })(this); | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
src/segment-controller.js
0 → 100644
1 | (function(window) { | ||
2 | |||
3 | var SegmentParser = window.videojs.hls.SegmentParser; | ||
4 | |||
5 | window.videojs.hls.SegmentController = function(){ | ||
6 | |||
7 | var self = this; | ||
8 | var url; | ||
9 | var parser; | ||
10 | var requestTimestamp; | ||
11 | var responseTimestamp; | ||
12 | var data; | ||
13 | |||
14 | var onDataCallback; | ||
15 | var onErrorCallback; | ||
16 | var onUpdateCallback; | ||
17 | |||
18 | self.loadSegment = function ( segmentUrl, onDataCallback, onErrorCallback, onUpdateCallback ) { | ||
19 | self.url = segmentUrl; | ||
20 | self.onDataCallback = onDataCallback; | ||
21 | self.onErrorCallback = onErrorCallback; | ||
22 | self.onUpdateCallback = onUpdateCallback; | ||
23 | self.requestTimestamp = new Date().getTime(); | ||
24 | |||
25 | vjs.get(segmentUrl, self.onSegmentLoadComplete, self.onSegmentLoadError); | ||
26 | }; | ||
27 | |||
28 | self.parseSegment = function ( incomingData ) { | ||
29 | // Add David's code later // | ||
30 | |||
31 | self.data = {}; | ||
32 | self.data.url = self.url; | ||
33 | self.data.isCached = false; | ||
34 | self.data.requestTimestamp = self.requestTimestamp; | ||
35 | self.data.responseTimestamp = self.responseTimestamp; | ||
36 | self.data.byteLength = incomingData.byteLength; | ||
37 | self.data.isCached = ( parseInt(self.responseTimestamp - self.requestTimestamp) < 75 ); | ||
38 | self.data.throughput = self.calculateThroughput(self.data.byteLength, self.requestTimestamp ,self.responseTimestamp) | ||
39 | |||
40 | return self.data; | ||
41 | }; | ||
42 | |||
43 | self.calculateThroughput = function(dataAmount, startTime, endTime) { | ||
44 | return Math.round(dataAmount/(endTime-startTime)*1000)*8; | ||
45 | } | ||
46 | |||
47 | self.onSegmentLoadComplete = function(response) { | ||
48 | self.responseTimestamp = new Date().getTime(); | ||
49 | |||
50 | var output = self.parseSegment(response); | ||
51 | |||
52 | if(self.onDataCallback != undefined) | ||
53 | { | ||
54 | self.onDataCallback(output); | ||
55 | } | ||
56 | }; | ||
57 | |||
58 | self.onSegmentLoadError = function(err) { | ||
59 | if(err) | ||
60 | { | ||
61 | console.log(err.message); | ||
62 | } | ||
63 | |||
64 | if(self.onErrorCallback != undefined) | ||
65 | { | ||
66 | onErrorCallback((err != undefined) ? err : null); | ||
67 | } | ||
68 | }; | ||
69 | } | ||
70 | })(this); |
test/manifest/brightcove_playlist_m3u8.js
0 → 100644
1 | window.brightcove_playlist_data = '#EXTM3U\n'+ | ||
2 | '#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=240000,RESOLUTION=396x224\n'+ | ||
3 | 'http://c.brightcove.com/services/mobile/streaming/index/rendition.m3u8?assetId=1824686811001&videoId=1824650741001\n'+ | ||
4 | '#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=40000\n'+ | ||
5 | 'http://c.brightcove.com/services/mobile/streaming/index/rendition.m3u8?assetId=1824683759001&videoId=1824650741001\n'+ | ||
6 | '#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=440000,RESOLUTION=396x224\n'+ | ||
7 | 'http://c.brightcove.com/services/mobile/streaming/index/rendition.m3u8?assetId=1824686593001&videoId=1824650741001\n'+ | ||
8 | '#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1928000,RESOLUTION=960x540\n'+ | ||
9 | 'http://c.brightcove.com/services/mobile/streaming/index/rendition.m3u8?assetId=1824687660001&videoId=1824650741001' | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
test/manifest/playlist.m3u8
0 → 100644
1 | #EXTM3U | ||
2 | #EXT-X-TARGETDURATION:10 | ||
3 | #EXT-X-VERSION:4 | ||
4 | #EXT-X-MEDIA-SEQUENCE:0 | ||
5 | #EXT-X-PLAYLIST-TYPE:VOD | ||
6 | #EXTINF:10, | ||
7 | #EXT-X-BYTERANGE:522828@0 | ||
8 | hls_450k_video.ts | ||
9 | #EXTINF:10, | ||
10 | #EXT-X-BYTERANGE:587500@522828 | ||
11 | hls_450k_video.ts | ||
12 | #EXTINF:10, | ||
13 | #EXT-X-BYTERANGE:713084@1110328 | ||
14 | hls_450k_video.ts | ||
15 | #EXTINF:10, | ||
16 | #EXT-X-BYTERANGE:476580@1823412 | ||
17 | hls_450k_video.ts | ||
18 | #EXTINF:10, | ||
19 | #EXT-X-BYTERANGE:535612@2299992 | ||
20 | hls_450k_video.ts | ||
21 | #EXTINF:10, | ||
22 | #EXT-X-BYTERANGE:207176@2835604 | ||
23 | hls_450k_video.ts | ||
24 | #EXTINF:10, | ||
25 | #EXT-X-BYTERANGE:455900@3042780 | ||
26 | hls_450k_video.ts | ||
27 | #EXTINF:10, | ||
28 | #EXT-X-BYTERANGE:657248@3498680 | ||
29 | hls_450k_video.ts | ||
30 | #EXTINF:10, | ||
31 | #EXT-X-BYTERANGE:571708@4155928 | ||
32 | hls_450k_video.ts | ||
33 | #EXTINF:10, | ||
34 | #EXT-X-BYTERANGE:485040@4727636 | ||
35 | hls_450k_video.ts | ||
36 | #EXTINF:10, | ||
37 | #EXT-X-BYTERANGE:709136@5212676 | ||
38 | hls_450k_video.ts | ||
39 | #EXTINF:10, | ||
40 | #EXT-X-BYTERANGE:730004@5921812 | ||
41 | hls_450k_video.ts | ||
42 | #EXTINF:10, | ||
43 | #EXT-X-BYTERANGE:456276@6651816 | ||
44 | hls_450k_video.ts | ||
45 | #EXTINF:10, | ||
46 | #EXT-X-BYTERANGE:468684@7108092 | ||
47 | hls_450k_video.ts | ||
48 | #EXTINF:10, | ||
49 | #EXT-X-BYTERANGE:444996@7576776 | ||
50 | hls_450k_video.ts | ||
51 | #EXTINF:10, | ||
52 | #EXT-X-BYTERANGE:331444@8021772 | ||
53 | hls_450k_video.ts | ||
54 | #EXTINF:1.4167, | ||
55 | #EXT-X-BYTERANGE:44556@8353216 | ||
56 | hls_450k_video.ts | ||
57 | #EXT-X-ENDLIST |
test/manifest/playlistM3U8data.js
0 → 100644
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 |
... | @@ -44,15 +44,30 @@ | ... | @@ -44,15 +44,30 @@ |
44 | <script src="../src/h264-stream.js"></script> | 44 | <script src="../src/h264-stream.js"></script> |
45 | <script src="../src/aac-stream.js"></script> | 45 | <script src="../src/aac-stream.js"></script> |
46 | <script src="../src/segment-parser.js"></script> | 46 | <script src="../src/segment-parser.js"></script> |
47 | <!-- an example MPEG2-TS segment --> | 47 | |
48 | <!-- <script src="tsSegment.js"></script> --> | 48 | <!-- M3U8 --> |
49 | <script src="../src/m3u8/m3u8.js"></script> | ||
50 | <script src="../src/m3u8/m3u8-tag-types.js"></script> | ||
51 | <script src="../src/m3u8/m3u8-parser.js"></script> | ||
52 | <script src="../src/manifest-controller.js"></script> | ||
53 | <!-- M3U8 TEST DATA --> | ||
54 | <script src="manifest/playlistM3U8data.js"></script> | ||
55 | <script src="manifest/brightcove_playlist_m3u8.js"></script> | ||
56 | <!-- M3U8 --> | ||
57 | <!-- SEGMENT --> | ||
49 | <script src="tsSegment-bc.js"></script> | 58 | <script src="tsSegment-bc.js"></script> |
59 | <script src="../src/segment-controller.js"></script> | ||
50 | 60 | ||
51 | <script src="../src/bin-utils.js"></script> | 61 | <script src="../src/bin-utils.js"></script> |
52 | 62 | ||
63 | |||
64 | |||
65 | |||
66 | |||
67 | <script src="exp-golomb_test.js"></script> | ||
53 | <script src="video-js-hls_test.js"></script> | 68 | <script src="video-js-hls_test.js"></script> |
54 | <script src="exp-golomb_test.js"></script> | 69 | <script src="exp-golomb_test.js"></script> |
55 | <script src="flv-tag_test.js"></script> | 70 | <script src="flv-tag_test.js"></script> |
56 | </head> | 71 | </head> |
57 | <body> | 72 | <body> |
58 | <div id="qunit"></div> | 73 | <div id="qunit"></div> | ... | ... |
... | @@ -20,6 +20,9 @@ | ... | @@ -20,6 +20,9 @@ |
20 | throws(block, [expected], [message]) | 20 | throws(block, [expected], [message]) |
21 | */ | 21 | */ |
22 | var | 22 | var |
23 | manifestController, | ||
24 | segmentController, | ||
25 | m3u8parser, | ||
23 | parser, | 26 | parser, |
24 | 27 | ||
25 | expectedHeader = [ | 28 | expectedHeader = [ |
... | @@ -95,7 +98,6 @@ | ... | @@ -95,7 +98,6 @@ |
95 | 98 | ||
96 | // payload starts at tag.bytes[16] | 99 | // payload starts at tag.bytes[16] |
97 | 100 | ||
98 | console.log(frameType); | ||
99 | 101 | ||
100 | // XXX: I'm not sure that frame types 3-5 are invalid | 102 | // XXX: I'm not sure that frame types 3-5 are invalid |
101 | ok(frameType === 1 || frameType === 2, | 103 | ok(frameType === 1 || frameType === 2, |
... | @@ -233,4 +235,152 @@ | ... | @@ -233,4 +235,152 @@ |
233 | 'the size of the previous tag is correct'); | 235 | 'the size of the previous tag is correct'); |
234 | } | 236 | } |
235 | }); | 237 | }); |
236 | })(this); | 238 | |
239 | /* | ||
240 | M3U8 Test Suite | ||
241 | */ | ||
242 | |||
243 | module('m3u8 parser', { | ||
244 | setup: function() { | ||
245 | m3u8parser = new window.videojs.hls.M3U8Parser(); | ||
246 | } | ||
247 | }); | ||
248 | |||
249 | test('should create my parser', function() { | ||
250 | ok(m3u8parser != undefined); | ||
251 | } | ||
252 | ); | ||
253 | |||
254 | test('should successfully parse manifest data', function() { | ||
255 | var parsedData = m3u8parser.parse(window.playlistData); | ||
256 | ok(parsedData); | ||
257 | } | ||
258 | ); | ||
259 | |||
260 | test('test for expected results', function() { | ||
261 | var data = m3u8parser.parse(window.playlistData); | ||
262 | |||
263 | notEqual(data, null, 'data is not NULL'); | ||
264 | equal(data.invalidReasons.length, 0,'data has 0 invalid reasons'); | ||
265 | equal(data.hasValidM3UTag, true, 'data has valid EXTM3U'); | ||
266 | equal(data.targetDuration, 10, 'data has correct TARGET DURATION'); | ||
267 | equal(data.allowCache, "NO", 'acceptable ALLOW CACHE'); | ||
268 | equal(data.isPlaylist, true, 'data is parsed as a PLAYLIST as expected'); | ||
269 | equal(data.playlistType, "VOD", 'acceptable PLAYLIST TYPE'); | ||
270 | equal(data.mediaItems.length, 16, 'acceptable mediaItem count'); | ||
271 | equal(data.mediaSequence, 0, 'MEDIA SEQUENCE is correct'); | ||
272 | equal(data.totalDuration, -1, "ZEN TOTAL DURATION is unknown as expected"); | ||
273 | equal(data.hasEndTag, true, 'should have ENDLIST tag'); | ||
274 | } | ||
275 | ); | ||
276 | |||
277 | module('brightcove playlist', { | ||
278 | setup: function() { | ||
279 | m3u8parser = new window.videojs.hls.M3U8Parser(); | ||
280 | } | ||
281 | }); | ||
282 | |||
283 | test('should parse a brightcove manifest data', function() { | ||
284 | var data = m3u8parser.parse(window.brightcove_playlist_data); | ||
285 | |||
286 | ok(data); | ||
287 | equal(data.playlistItems.length, 4, 'Has correct rendition count'); | ||
288 | equal(data.playlistItems[0].bandwidth, 240000, 'First rendition index bandwidth is correct' ); | ||
289 | equal(data.playlistItems[0]["program-id"], 1, 'First rendition index program-id is correct' ); | ||
290 | equal(data.playlistItems[0].resolution.width, 396, 'First rendition index resolution width is correct' ); | ||
291 | equal(data.playlistItems[0].resolution.height, 224, 'First rendition index resolution height is correct' ); | ||
292 | |||
293 | } | ||
294 | ); | ||
295 | |||
296 | module('manifest controller', { | ||
297 | setup: function() { | ||
298 | manifestController = new window.videojs.hls.ManifestController(); | ||
299 | this.vjsget = vjs.get; | ||
300 | vjs.get = function(url, success, error){ | ||
301 | console.log(url); | ||
302 | success(window.brightcove_playlist_data); | ||
303 | }; | ||
304 | }, | ||
305 | teardown: function() { | ||
306 | vjs.get = this.vjsget; | ||
307 | } | ||
308 | }); | ||
309 | |||
310 | test('should create', function() { | ||
311 | ok(manifestController); | ||
312 | }); | ||
313 | |||
314 | test('should return a parsed object', function() { | ||
315 | var data = manifestController.parseManifest(window.brightcove_playlist_data); | ||
316 | |||
317 | ok(data); | ||
318 | |||
319 | equal(data.playlistItems.length, 4, 'Has correct rendition count'); | ||
320 | equal(data.playlistItems[0].bandwidth, 240000, 'First rendition index bandwidth is correct' ); | ||
321 | equal(data.playlistItems[0]["program-id"], 1, 'First rendition index program-id is correct' ); | ||
322 | equal(data.playlistItems[0].resolution.width, 396, 'First rendition index resolution width is correct' ); | ||
323 | equal(data.playlistItems[0].resolution.height, 224, 'First rendition index resolution height is correct' ); | ||
324 | }) | ||
325 | |||
326 | test('should get a manifest from hermes', function() { | ||
327 | var hermesUrl = "http://localhost:7070/test/basic-playback/brightcove/16x9-master.m3u8"; | ||
328 | |||
329 | manifestController.loadManifest( | ||
330 | hermesUrl, | ||
331 | function(responseData){ | ||
332 | ok(true); | ||
333 | }, | ||
334 | function(errorData){ | ||
335 | console.log('got error data'); | ||
336 | }, | ||
337 | function(updateData){ | ||
338 | console.log('got update data'); | ||
339 | } | ||
340 | ) | ||
341 | }); | ||
342 | |||
343 | module('segment controller', { | ||
344 | setup: function() { | ||
345 | segmentController = new window.videojs.hls.SegmentController(); | ||
346 | this.vjsget = vjs.get; | ||
347 | vjs.get = function(url, success, error){ | ||
348 | console.log('load segment url', url); | ||
349 | success(window.bcSegment); | ||
350 | }; | ||
351 | }, | ||
352 | teardown: function() { | ||
353 | vjs.get = this.vjsget; | ||
354 | } | ||
355 | }); | ||
356 | |||
357 | test('should get a segment data', function() { | ||
358 | ok(true); | ||
359 | var hermesUrl = "http://localhost:7070/test/ts-files/brightcove/s-1.ts"; | ||
360 | |||
361 | segmentController.loadSegment( | ||
362 | hermesUrl, | ||
363 | function(responseData){ | ||
364 | console.log('got response from segment controller'); | ||
365 | ok(true); | ||
366 | |||
367 | }, | ||
368 | function(errorData){ | ||
369 | console.log('got error data'); | ||
370 | }, | ||
371 | function(updateData){ | ||
372 | console.log('got update data'); | ||
373 | } | ||
374 | ) | ||
375 | } | ||
376 | ) | ||
377 | |||
378 | test('bandwidth calulation test', function() { | ||
379 | var multiSecondData = segmentController.calculateThroughput(10000,1000,2000); | ||
380 | var subSecondData = segmentController.calculateThroughput(10000,1000,1500); | ||
381 | equal(multiSecondData, 80000, 'MULTI-Second bits per second calculation'); | ||
382 | equal(subSecondData, 160000, 'SUB-Second bits per second calculation'); | ||
383 | |||
384 | }) | ||
385 | |||
386 | })(this); | ||
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
video-js-hls.iml
0 → 100644
-
Please register or sign in to post a comment