Add m3u8.pegjs.
Generate the parser file via 'npm run peg'. There is a simple test script in test/pegtest.js. The test can be run via 'npm run testpeg' which will generate a new copy of the parser and then run the test file.
Showing
3 changed files
with
281 additions
and
2 deletions
... | @@ -6,7 +6,10 @@ | ... | @@ -6,7 +6,10 @@ |
6 | }, | 6 | }, |
7 | "license": "Apache 2", | 7 | "license": "Apache 2", |
8 | "scripts": { | 8 | "scripts": { |
9 | "test": "grunt qunit" | 9 | "test": "grunt qunit", |
10 | "prepublish": "npm run peg", | ||
11 | "peg": "pegjs src/m3u8/m3u8.pegjs src/m3u8/m3u8-generated.js", | ||
12 | "testpeg": "npm run peg && node test/pegtest.js" | ||
10 | }, | 13 | }, |
11 | "devDependencies": { | 14 | "devDependencies": { |
12 | "grunt-contrib-jshint": "~0.6.0", | 15 | "grunt-contrib-jshint": "~0.6.0", |
... | @@ -15,7 +18,8 @@ | ... | @@ -15,7 +18,8 @@ |
15 | "grunt-contrib-uglify": "~0.2.0", | 18 | "grunt-contrib-uglify": "~0.2.0", |
16 | "grunt-contrib-watch": "~0.4.0", | 19 | "grunt-contrib-watch": "~0.4.0", |
17 | "grunt-contrib-clean": "~0.4.0", | 20 | "grunt-contrib-clean": "~0.4.0", |
18 | "grunt": "~0.4.1" | 21 | "grunt": "~0.4.1", |
22 | "pegjs": "~0.7.0" | ||
19 | }, | 23 | }, |
20 | "dependencies": { | 24 | "dependencies": { |
21 | "video.js": "~4.2.2", | 25 | "video.js": "~4.2.2", | ... | ... |
src/m3u8/m3u8.pegjs
0 → 100644
1 | /***** Start *****/ | ||
2 | start | ||
3 | = tags:tags+ .* { | ||
4 | var obj = {}; | ||
5 | tags.forEach(function(tag) { for (var p in tag) { obj[p] = tag[p]; }}); | ||
6 | return obj; | ||
7 | } | ||
8 | |||
9 | tags | ||
10 | = tag:m3uTag _ { return tag; } | ||
11 | / tag:extinfTag _ { return tag; } | ||
12 | / tag:targetDurationTag _ { return tag; } | ||
13 | / tag:mediaSequenceTag _ { return tag; } | ||
14 | / tag:keyTag _ { return tag; } | ||
15 | / tag:programDateTimeTag _ { return tag; } | ||
16 | / tag:allowCacheTag _ { return tag; } | ||
17 | / tag:playlistTypeTag _ { return tag; } | ||
18 | / tag:endlistTag _ { return tag; } | ||
19 | / tag:mediaTag _ { return tag; } | ||
20 | / tag:streamInfTag _ { return tag; } | ||
21 | / tag:discontinuityTag _ { return tag; } | ||
22 | / tag:discontinuitySequenceTag _ { return tag; } | ||
23 | / tag:iframesOnlyTag _ { return tag; } | ||
24 | / tag:mapTag _ { return tag; } | ||
25 | / tag:iframeStreamInf _ { return tag; } | ||
26 | / tag:startTag _ { return tag; } | ||
27 | / tag:versionTag _ { return tag; } | ||
28 | |||
29 | /***** Tags *****/ | ||
30 | |||
31 | m3uTag | ||
32 | = tag:"#EXTM3U" { return {openTag: true}; } | ||
33 | |||
34 | extinfTag | ||
35 | = tag:'#EXTINF' ":" duration:number "," _ title:text? _ byteRange:byteRangeTag? file:mediaFile { | ||
36 | var fileObj = {}; | ||
37 | fileObj[file] = { | ||
38 | byteRange: byteRange, | ||
39 | title: title, | ||
40 | duration: duration, | ||
41 | file: file | ||
42 | }; | ||
43 | return fileObj; | ||
44 | } | ||
45 | |||
46 | byteRangeTag | ||
47 | = tag:"EXT-X-BYTERANGE" ":" length:int ("@" offset:int)? { return {length: length, offset: offset}; } | ||
48 | |||
49 | targetDurationTag | ||
50 | = tag:"#EXT-X-TARGETDURATION" ":" seconds:int { return {targetDuration: seconds}; } | ||
51 | |||
52 | mediaSequenceTag | ||
53 | = tag:'#EXT-X-MEDIA-SEQUENCE' ":" sequenceNumber:int { return {mediaSequence: sequenceNumber}; } | ||
54 | |||
55 | keyTag | ||
56 | = tag:'#EXT-X-KEY' ":" attrs:keyAttributes { return {key: attrs}; } | ||
57 | |||
58 | programDateTimeTag | ||
59 | = tag:'#EXT-X-PROGRAM-DATE-TIME' ":" date:date | ||
60 | |||
61 | allowCacheTag | ||
62 | = tag:'#EXT-X-ALLOW-CACHE' ":" answer:answer { return {allowCache: answer}; } | ||
63 | |||
64 | playlistTypeTag | ||
65 | = tag:'#EXT-X-PLAYLIST-TYPE' ":" type:playlistType { return {playlistType: type}; } | ||
66 | |||
67 | endlistTag | ||
68 | = tag:'#EXT-X-ENDLIST' { return {closeTag: true}; } | ||
69 | |||
70 | mediaTag | ||
71 | = tag:'#EXT-MEDIA' ":" attrs:mediaAttributes { return {media: attrs}; } | ||
72 | |||
73 | streamInfTag | ||
74 | = tag:'#EXT-X-STREAM-INF' ":" attrs:streamInfAttrs _ file:mediaFile { | ||
75 | var fileObj = {}; | ||
76 | fileObj[file] = { | ||
77 | attributes: attrs, | ||
78 | file: file | ||
79 | }; | ||
80 | return fileObj; | ||
81 | } | ||
82 | |||
83 | discontinuityTag | ||
84 | = tag:'#EXT-X-DISCONTINUITY' | ||
85 | |||
86 | discontinuitySequenceTag | ||
87 | = tag:'#EXT-X-DISCONTINUITY-SEQUENCE' ":" sequence:int { return {discontinuitySequence: sequence}; } | ||
88 | |||
89 | iframesOnlyTag | ||
90 | = tag:'#EXT-X-I-FRAMES-ONLY' | ||
91 | |||
92 | mapTag | ||
93 | = tag:'#EXT-X-MAP' ":" attrs:mapAttributes { return {map: attrs}; } | ||
94 | |||
95 | iframeStreamInf | ||
96 | = tag:'#EXT-X-I-FRAME-STREAM-INF' ":" attrs:iframeStreamAttrs { return {iframeStream: attrs}; } | ||
97 | |||
98 | startTag | ||
99 | = tag:'EXT-X-START' ":" attrs:startAttributes { return {start: attrs}; } | ||
100 | |||
101 | versionTag | ||
102 | = tag:'#EXT-X-VERSION' ":" version:int { return {version: version}; } | ||
103 | |||
104 | /***** Helpers *****/ | ||
105 | |||
106 | mediaFile | ||
107 | = file:[ -~]+ { return file.join(''); } | ||
108 | |||
109 | keyAttributes | ||
110 | = attrs:keyAttribute+ | ||
111 | |||
112 | keyAttribute | ||
113 | = "METHOD" "=" method:keyMethod | ||
114 | / "URI" "=" uri:quotedString | ||
115 | / "IV" "=" iv:hexint | ||
116 | / "KEYFORMAT" "=" keyFormat:quotedString | ||
117 | / "KEYFORMATVERSIONS" "=" keyFormatVersions:quotedString | ||
118 | |||
119 | keyMethod | ||
120 | = "NONE" | ||
121 | / "AES-128" | ||
122 | / "SAMPLE-AES" | ||
123 | |||
124 | mediaAttributes | ||
125 | = attrs:mediaAttribute+ | ||
126 | |||
127 | mediaAttribute | ||
128 | = "TYPE" "=" type:enumeratedString | ||
129 | / "URI" "=" uri:quotedString | ||
130 | / "GROUP-ID" "=" groupId:quotedString | ||
131 | / "LANGUAGE" "=" langauge:quotedString | ||
132 | / "ASSOC-LANGUAGE" "=" assocLanguage:quotedString | ||
133 | / "NAME" "=" name:quotedString | ||
134 | / "DEFAULT" "=" default:answer | ||
135 | / "AUTOSELECT" "="autoselect:answer | ||
136 | / "FORCE" "=" force:answer | ||
137 | / "INSTREAM-ID" "=" instreamId:quotedString | ||
138 | / "CHARACTERISTICS" "=" characteristics:quotedString | ||
139 | |||
140 | streamInfAttrs | ||
141 | = attrs:streamInfAttr+ | ||
142 | |||
143 | streamInfAttr | ||
144 | = streamInfSharedAttr | ||
145 | / "AUDIO" "=" audio:quotedString | ||
146 | / "SUBTITLES" "=" subtitles:quotedString | ||
147 | / "CLOSED-CAPTION" "=" captions:"NONE" | ||
148 | / "CLOSED-CAPTION" "=" captions:quotedString | ||
149 | |||
150 | streamInfSharedAttr | ||
151 | = "BANDWIDTH" "=" bandwidth:int | ||
152 | / "CODECS" "=" codec:quotedString | ||
153 | / "RESOLUTION" "=" resolution:resolution | ||
154 | / "VIDEO" "=" video:quotedString | ||
155 | |||
156 | mapAttributes | ||
157 | = attrs:mapAttribute+ | ||
158 | |||
159 | mapAttribute | ||
160 | = "URI" "=" uri:quotedString | ||
161 | / "BYTERANGE" "=" byteRange:quotedString | ||
162 | |||
163 | iframeStreamAttrs | ||
164 | = iframeStreamAttr+ | ||
165 | |||
166 | iframeStreamAttr | ||
167 | = streamInfSharedAttr | ||
168 | / "URI" "=" uri:quotedString | ||
169 | |||
170 | startAttributes | ||
171 | = attrs:startAttribute+ | ||
172 | |||
173 | startAttribute | ||
174 | = "TIME-OFFSET" "=" timeOffset:number | ||
175 | / "PRECISE" "=" precise:answer | ||
176 | |||
177 | answer "answer" | ||
178 | = "YES" | ||
179 | / "NO" | ||
180 | |||
181 | playlistType | ||
182 | = "EVENT" | ||
183 | / "VOD" | ||
184 | |||
185 | /***** Date *****/ | ||
186 | |||
187 | date "date" | ||
188 | = year:year "-" month:month "-" day:day "T" time:time timezone:timezone | ||
189 | |||
190 | year "year" | ||
191 | = digit digit digit digit | ||
192 | |||
193 | month "month" | ||
194 | = [01] digit | ||
195 | |||
196 | day "day" | ||
197 | = [0-3] digit | ||
198 | |||
199 | time "time" | ||
200 | = [0-2] digit ":" [0-5] digit ":" [0-5] digit "." digit+ | ||
201 | / [0-2] digit ":" [0-5] digit ":" [0-5] digit | ||
202 | / [0-2] digit ":" [0-5] digit | ||
203 | |||
204 | timezone "timezone" | ||
205 | = [+-] [0-2] digit ":" [0-5] digit | ||
206 | / "Z" | ||
207 | |||
208 | /***** Numbers *****/ | ||
209 | |||
210 | number "number" | ||
211 | = parts:(int frac) _ { return parseFloat(parts.join('')); } | ||
212 | / parts:(int) _ { return parts; } | ||
213 | |||
214 | resolution | ||
215 | = int "x" int | ||
216 | |||
217 | int | ||
218 | = first:digit19 rest:digits { return parseInt(first + rest.join(''), 10); } | ||
219 | / digit:digit { return parseInt(digit, 10); } | ||
220 | / neg:"-" first:digit19 rest:digits { return parseInt(neg + first + rest.join(''), 10); } | ||
221 | / neg:"-" digit { return parseInt(neg + digit, 10); } | ||
222 | |||
223 | hexint | ||
224 | = "0x" hexDigits:hexDigit+ { return '0x' + hexDigits.join(''); } | ||
225 | / "0X" hexDigits:hexDigit+ { return '0x' + hexDigits.join(''); } | ||
226 | |||
227 | frac | ||
228 | = dec:"." digits:digits { return parseFloat(dec + digits.join('')); } | ||
229 | |||
230 | digits | ||
231 | = digit+ | ||
232 | |||
233 | digit | ||
234 | = [0-9] | ||
235 | |||
236 | digit19 | ||
237 | = [1-9] | ||
238 | |||
239 | hexDigit | ||
240 | = [0-9a-fA-F] | ||
241 | |||
242 | /***** Text *****/ | ||
243 | |||
244 | enumeratedString | ||
245 | = chars:enumeratedChar+ _ { return chars.join('') } | ||
246 | |||
247 | quotedString | ||
248 | = '"' '"' _ { return ""; } | ||
249 | / '"' chars:quotedChar+ '"' _ { return chars.join(''); } | ||
250 | |||
251 | enumeratedChar | ||
252 | = [^'" \n\t\r] | ||
253 | / [a-zA-Z0-9] | ||
254 | |||
255 | quotedChar | ||
256 | = [^\r\n"] | ||
257 | / char:char | ||
258 | |||
259 | text "text" | ||
260 | = text:char+ { return text.join(''); } | ||
261 | |||
262 | char "char" | ||
263 | = [ -~] | ||
264 | |||
265 | _ "whitespace" | ||
266 | = whitespace* | ||
267 | |||
268 | whitespace | ||
269 | = [ \t\n\r] |
-
Please register or sign in to post a comment