Parse PES packets
Transfer the logic to parse elementary stream packets from segment-parser into the MP2T parse stream.
Showing
2 changed files
with
178 additions
and
4 deletions
... | @@ -70,7 +70,7 @@ PacketStream.prototype = new videojs.Hls.Stream(); | ... | @@ -70,7 +70,7 @@ PacketStream.prototype = new videojs.Hls.Stream(); |
70 | * forms of the individual packets. | 70 | * forms of the individual packets. |
71 | */ | 71 | */ |
72 | ParseStream = function() { | 72 | ParseStream = function() { |
73 | var parsePsi, parsePat, parsePmt, self; | 73 | var parsePsi, parsePat, parsePmt, parsePes, self; |
74 | PacketStream.prototype.init.call(this); | 74 | PacketStream.prototype.init.call(this); |
75 | self = this; | 75 | self = this; |
76 | 76 | ||
... | @@ -141,6 +141,51 @@ ParseStream = function() { | ... | @@ -141,6 +141,51 @@ ParseStream = function() { |
141 | // skip past the elementary stream descriptors, if present | 141 | // skip past the elementary stream descriptors, if present |
142 | offset += ((payload[offset + 3] & 0x0F) << 8 | payload[offset + 4]) + 5; | 142 | offset += ((payload[offset + 3] & 0x0F) << 8 | payload[offset + 4]) + 5; |
143 | } | 143 | } |
144 | |||
145 | // record the map on the packet as well | ||
146 | pmt.programMapTable = self.programMapTable; | ||
147 | }; | ||
148 | |||
149 | parsePes = function(payload, pes) { | ||
150 | var dataAlignmentIndicator, ptsDtsFlags; | ||
151 | // find out if this packets starts a new keyframe | ||
152 | dataAlignmentIndicator = (payload[6] & 0x04) !== 0; | ||
153 | // PES packets may be annotated with a PTS value, or a PTS value | ||
154 | // and a DTS value. Determine what combination of values is | ||
155 | // available to work with. | ||
156 | ptsDtsFlags = payload[7]; | ||
157 | |||
158 | // PTS and DTS are normally stored as a 33-bit number. Javascript | ||
159 | // performs all bitwise operations on 32-bit integers but it's | ||
160 | // convenient to convert from 90ns to 1ms time scale anyway. So | ||
161 | // what we are going to do instead is drop the least significant | ||
162 | // bit (in effect, dividing by two) then we can divide by 45 (45 * | ||
163 | // 2 = 90) to get ms. | ||
164 | if (ptsDtsFlags & 0xC0) { | ||
165 | // the PTS and DTS are not written out directly. For information | ||
166 | // on how they are encoded, see | ||
167 | // http://dvd.sourceforge.net/dvdinfo/pes-hdr.html | ||
168 | pes.pts = (payload[9] & 0x0E) << 28 | ||
169 | | (payload[10] & 0xFF) << 21 | ||
170 | | (payload[11] & 0xFE) << 13 | ||
171 | | (payload[12] & 0xFF) << 6 | ||
172 | | (payload[13] & 0xFE) >>> 2; | ||
173 | pes.pts /= 45; | ||
174 | pes.dts = pes.pts; | ||
175 | if (ptsDtsFlags & 0x40) { | ||
176 | pes.dts = (payload[14] & 0x0E ) << 28 | ||
177 | | (payload[15] & 0xFF ) << 21 | ||
178 | | (payload[16] & 0xFE ) << 13 | ||
179 | | (payload[17] & 0xFF ) << 6 | ||
180 | | (payload[18] & 0xFE ) >>> 2; | ||
181 | pes.dts /= 45; | ||
182 | } | ||
183 | } | ||
184 | |||
185 | // the data section starts immediately after the PES header. | ||
186 | // pes_header_data_length specifies the number of header bytes | ||
187 | // that follow the last byte of the field. | ||
188 | pes.data = payload.subarray(9 + payload[8]); | ||
144 | }; | 189 | }; |
145 | 190 | ||
146 | /** | 191 | /** |
... | @@ -149,8 +194,7 @@ ParseStream = function() { | ... | @@ -149,8 +194,7 @@ ParseStream = function() { |
149 | this.push = function(packet) { | 194 | this.push = function(packet) { |
150 | var | 195 | var |
151 | result = {}, | 196 | result = {}, |
152 | offset = 4, | 197 | offset = 4; |
153 | stream; | ||
154 | // make sure packet is aligned on a sync byte | 198 | // make sure packet is aligned on a sync byte |
155 | if (packet[0] !== 0x47) { | 199 | if (packet[0] !== 0x47) { |
156 | return this.trigger('error', 'mis-aligned packet'); | 200 | return this.trigger('error', 'mis-aligned packet'); |
... | @@ -178,8 +222,9 @@ ParseStream = function() { | ... | @@ -178,8 +222,9 @@ ParseStream = function() { |
178 | result.type = 'pmt'; | 222 | result.type = 'pmt'; |
179 | parsePsi(packet.subarray(offset), result); | 223 | parsePsi(packet.subarray(offset), result); |
180 | } else { | 224 | } else { |
181 | result.stream = this.programMapTable[result.pid]; | 225 | result.streamType = this.programMapTable[result.pid]; |
182 | result.type = 'pes'; | 226 | result.type = 'pes'; |
227 | parsePes(packet.subarray(offset), result); | ||
183 | } | 228 | } |
184 | 229 | ||
185 | this.trigger('data', result); | 230 | this.trigger('data', result); | ... | ... |
... | @@ -220,6 +220,7 @@ test('parses the program map table pid from the program association table (PAT)' | ... | @@ -220,6 +220,7 @@ test('parses the program map table pid from the program association table (PAT)' |
220 | // crc32:0000 0000 0000 0000 0000 0000 0000 0000 | 220 | // crc32:0000 0000 0000 0000 0000 0000 0000 0000 |
221 | 0x00, 0x00, 0x00, 0x00 | 221 | 0x00, 0x00, 0x00, 0x00 |
222 | ])); | 222 | ])); |
223 | ok(packet, 'parsed a packet'); | ||
223 | strictEqual(0x0010, parseStream.pmtPid, 'parsed PMT pid'); | 224 | strictEqual(0x0010, parseStream.pmtPid, 'parsed PMT pid'); |
224 | }); | 225 | }); |
225 | 226 | ||
... | @@ -260,9 +261,137 @@ test('parse the elementary streams from a program map table', function() { | ... | @@ -260,9 +261,137 @@ test('parse the elementary streams from a program map table', function() { |
260 | 0x00, 0x00, 0x00, 0x00 | 261 | 0x00, 0x00, 0x00, 0x00 |
261 | ])); | 262 | ])); |
262 | 263 | ||
264 | ok(packet, 'parsed a packet'); | ||
263 | ok(parseStream.programMapTable, 'parsed a program map'); | 265 | ok(parseStream.programMapTable, 'parsed a program map'); |
264 | strictEqual(0x1b, parseStream.programMapTable[0x11], 'associated h264 with pid 0x11'); | 266 | strictEqual(0x1b, parseStream.programMapTable[0x11], 'associated h264 with pid 0x11'); |
265 | strictEqual(0x0f, parseStream.programMapTable[0x12], 'associated adts with pid 0x12'); | 267 | strictEqual(0x0f, parseStream.programMapTable[0x12], 'associated adts with pid 0x12'); |
268 | deepEqual(parseStream.programMapTable, packet.programMapTable, 'recorded the PMT'); | ||
269 | }); | ||
270 | |||
271 | test('parses an elementary stream packet with just a pts', function() { | ||
272 | var packet; | ||
273 | parseStream.on('data', function(data) { | ||
274 | packet = data; | ||
275 | }); | ||
276 | |||
277 | parseStream.programMapTable = { | ||
278 | 0x11: 0x1b // pid 0x11 is h264 data | ||
279 | }; | ||
280 | |||
281 | parseStream.push(new Uint8Array([ | ||
282 | 0x47, // sync byte | ||
283 | // tei:0 pusi:1 tp:0 pid:0 0000 0001 0001 | ||
284 | 0x40, 0x11, | ||
285 | // tsc:01 afc:01 cc:0000 | ||
286 | 0x50, | ||
287 | // pscp:0000 0000 0000 0000 0000 0001 | ||
288 | 0x00, 0x00, 0x01, | ||
289 | // sid:0000 0000 ppl:0000 0000 0000 1001 | ||
290 | 0x00, 0x00, 0x09, | ||
291 | // 10 psc:00 pp:0 dai:1 c:0 ooc:0 | ||
292 | 0x84, | ||
293 | // pdf:10 ef:1 erf:0 dtmf:0 acif:0 pcf:0 pef:0 | ||
294 | 0xc0, | ||
295 | // phdl:0000 0101 '0010' pts:000 mb:1 pts:0000 0000 | ||
296 | 0x05, 0x21, 0x00, | ||
297 | // pts:0000 000 mb:1 pts:0000 0000 pts:0000 000 mb:1 | ||
298 | 0x01, 0x00, 0x01, | ||
299 | // "data":0101 | ||
300 | 0x11 | ||
301 | ])); | ||
302 | |||
303 | ok(packet, 'parsed a packet'); | ||
304 | equal('pes', packet.type, 'recognized a PES packet'); | ||
305 | equal(0x1b, packet.streamType, 'tracked the stream_type'); | ||
306 | equal(1, packet.data.byteLength, 'parsed a single data byte'); | ||
307 | equal(0x11, packet.data[0], 'parsed the data'); | ||
308 | equal(0, packet.pts, 'parsed the pts'); | ||
309 | }); | ||
310 | |||
311 | test('parses an elementary stream packet with a pts and dts', function() { | ||
312 | var packet; | ||
313 | parseStream.on('data', function(data) { | ||
314 | packet = data; | ||
315 | }); | ||
316 | |||
317 | parseStream.programMapTable = { | ||
318 | 0x11: 0x1b // pid 0x11 is h264 data | ||
319 | }; | ||
320 | |||
321 | parseStream.push(new Uint8Array([ | ||
322 | 0x47, // sync byte | ||
323 | // tei:0 pusi:1 tp:0 pid:0 0000 0001 0001 | ||
324 | 0x40, 0x11, | ||
325 | // tsc:01 afc:01 cc:0000 | ||
326 | 0x50, | ||
327 | // pscp:0000 0000 0000 0000 0000 0001 | ||
328 | 0x00, 0x00, 0x01, | ||
329 | // sid:0000 0000 ppl:0000 0000 0000 1110 | ||
330 | 0x00, 0x00, 0x0e, | ||
331 | // 10 psc:00 pp:0 dai:1 c:0 ooc:0 | ||
332 | 0x84, | ||
333 | // pdf:11 ef:1 erf:0 dtmf:0 acif:0 pcf:0 pef:0 | ||
334 | 0xe0, | ||
335 | // phdl:0000 1010 '0011' pts:000 mb:1 pts:0000 0000 | ||
336 | 0x0a, 0x21, 0x00, | ||
337 | // pts:0000 000 mb:1 pts:0000 0000 pts:0000 100 mb:1 | ||
338 | 0x01, 0x00, 0x09, | ||
339 | // '0001' dts:000 mb:1 dts:0000 0000 dts:0000 000 mb:1 | ||
340 | 0x11, 0x00, 0x01, | ||
341 | // dts:0000 0000 dts:0000 010 mb:1 | ||
342 | 0x00, 0x05, | ||
343 | // "data":0101 | ||
344 | 0x11 | ||
345 | ])); | ||
346 | |||
347 | ok(packet, 'parsed a packet'); | ||
348 | equal('pes', packet.type, 'recognized a PES packet'); | ||
349 | equal(0x1b, packet.streamType, 'tracked the stream_type'); | ||
350 | equal(1, packet.data.byteLength, 'parsed a single data byte'); | ||
351 | equal(0x11, packet.data[0], 'parsed the data'); | ||
352 | equal(4 / 90, packet.pts, 'parsed the pts'); | ||
353 | equal(2 / 90, packet.dts, 'parsed the dts'); | ||
354 | }); | ||
355 | |||
356 | test('parses an elementary stream packet without a pts or dts', function() { | ||
357 | |||
358 | var packet; | ||
359 | parseStream.on('data', function(data) { | ||
360 | packet = data; | ||
361 | }); | ||
362 | |||
363 | parseStream.programMapTable = { | ||
364 | 0x11: 0x1b // pid 0x11 is h264 data | ||
365 | }; | ||
366 | |||
367 | parseStream.push(new Uint8Array([ | ||
368 | 0x47, // sync byte | ||
369 | // tei:0 pusi:1 tp:0 pid:0 0000 0001 0001 | ||
370 | 0x40, 0x11, | ||
371 | // tsc:01 afc:01 cc:0000 | ||
372 | 0x50, | ||
373 | // pscp:0000 0000 0000 0000 0000 0001 | ||
374 | 0x00, 0x00, 0x01, | ||
375 | // sid:0000 0000 ppl:0000 0000 0000 0101 | ||
376 | 0x00, 0x00, 0x05, | ||
377 | // 10 psc:00 pp:0 dai:1 c:0 ooc:0 | ||
378 | 0x84, | ||
379 | // pdf:00 ef:1 erf:0 dtmf:0 acif:0 pcf:0 pef:0 | ||
380 | 0x20, | ||
381 | // phdl:0000 0000 | ||
382 | 0x00, | ||
383 | // "data":1010 1111 0000 0001 | ||
384 | 0xaf, 0x01 | ||
385 | ])); | ||
386 | |||
387 | ok(packet, 'parsed a packet'); | ||
388 | equal('pes', packet.type, 'recognized a PES packet'); | ||
389 | equal(0x1b, packet.streamType, 'tracked the stream_type'); | ||
390 | equal(2, packet.data.byteLength, 'parsed two data bytes'); | ||
391 | equal(0xaf, packet.data[0], 'parsed the first data byte'); | ||
392 | equal(0x01, packet.data[1], 'parsed the second data byte'); | ||
393 | ok(!packet.pts, 'did not parse a pts'); | ||
394 | ok(!packet.dts, 'did not parse a dts'); | ||
266 | }); | 395 | }); |
267 | 396 | ||
268 | module('MP4 Transmuxer', { | 397 | module('MP4 Transmuxer', { | ... | ... |
-
Please register or sign in to post a comment