49f0fe0f by David LaPalomento

Parse PES packets

Transfer the logic to parse elementary stream packets from segment-parser into the MP2T parse stream.
1 parent 1bbcbe8d
...@@ -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', {
......