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();
* forms of the individual packets.
*/
ParseStream = function() {
var parsePsi, parsePat, parsePmt, self;
var parsePsi, parsePat, parsePmt, parsePes, self;
PacketStream.prototype.init.call(this);
self = this;
......@@ -141,6 +141,51 @@ ParseStream = function() {
// skip past the elementary stream descriptors, if present
offset += ((payload[offset + 3] & 0x0F) << 8 | payload[offset + 4]) + 5;
}
// record the map on the packet as well
pmt.programMapTable = self.programMapTable;
};
parsePes = function(payload, pes) {
var dataAlignmentIndicator, ptsDtsFlags;
// find out if this packets starts a new keyframe
dataAlignmentIndicator = (payload[6] & 0x04) !== 0;
// PES packets may be annotated with a PTS value, or a PTS value
// and a DTS value. Determine what combination of values is
// available to work with.
ptsDtsFlags = payload[7];
// PTS and DTS are normally stored as a 33-bit number. Javascript
// performs all bitwise operations on 32-bit integers but it's
// convenient to convert from 90ns to 1ms time scale anyway. So
// what we are going to do instead is drop the least significant
// bit (in effect, dividing by two) then we can divide by 45 (45 *
// 2 = 90) to get ms.
if (ptsDtsFlags & 0xC0) {
// the PTS and DTS are not written out directly. For information
// on how they are encoded, see
// http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
pes.pts = (payload[9] & 0x0E) << 28
| (payload[10] & 0xFF) << 21
| (payload[11] & 0xFE) << 13
| (payload[12] & 0xFF) << 6
| (payload[13] & 0xFE) >>> 2;
pes.pts /= 45;
pes.dts = pes.pts;
if (ptsDtsFlags & 0x40) {
pes.dts = (payload[14] & 0x0E ) << 28
| (payload[15] & 0xFF ) << 21
| (payload[16] & 0xFE ) << 13
| (payload[17] & 0xFF ) << 6
| (payload[18] & 0xFE ) >>> 2;
pes.dts /= 45;
}
}
// the data section starts immediately after the PES header.
// pes_header_data_length specifies the number of header bytes
// that follow the last byte of the field.
pes.data = payload.subarray(9 + payload[8]);
};
/**
......@@ -149,8 +194,7 @@ ParseStream = function() {
this.push = function(packet) {
var
result = {},
offset = 4,
stream;
offset = 4;
// make sure packet is aligned on a sync byte
if (packet[0] !== 0x47) {
return this.trigger('error', 'mis-aligned packet');
......@@ -178,8 +222,9 @@ ParseStream = function() {
result.type = 'pmt';
parsePsi(packet.subarray(offset), result);
} else {
result.stream = this.programMapTable[result.pid];
result.streamType = this.programMapTable[result.pid];
result.type = 'pes';
parsePes(packet.subarray(offset), result);
}
this.trigger('data', result);
......
......@@ -220,6 +220,7 @@ test('parses the program map table pid from the program association table (PAT)'
// crc32:0000 0000 0000 0000 0000 0000 0000 0000
0x00, 0x00, 0x00, 0x00
]));
ok(packet, 'parsed a packet');
strictEqual(0x0010, parseStream.pmtPid, 'parsed PMT pid');
});
......@@ -260,9 +261,137 @@ test('parse the elementary streams from a program map table', function() {
0x00, 0x00, 0x00, 0x00
]));
ok(packet, 'parsed a packet');
ok(parseStream.programMapTable, 'parsed a program map');
strictEqual(0x1b, parseStream.programMapTable[0x11], 'associated h264 with pid 0x11');
strictEqual(0x0f, parseStream.programMapTable[0x12], 'associated adts with pid 0x12');
deepEqual(parseStream.programMapTable, packet.programMapTable, 'recorded the PMT');
});
test('parses an elementary stream packet with just a pts', function() {
var packet;
parseStream.on('data', function(data) {
packet = data;
});
parseStream.programMapTable = {
0x11: 0x1b // pid 0x11 is h264 data
};
parseStream.push(new Uint8Array([
0x47, // sync byte
// tei:0 pusi:1 tp:0 pid:0 0000 0001 0001
0x40, 0x11,
// tsc:01 afc:01 cc:0000
0x50,
// pscp:0000 0000 0000 0000 0000 0001
0x00, 0x00, 0x01,
// sid:0000 0000 ppl:0000 0000 0000 1001
0x00, 0x00, 0x09,
// 10 psc:00 pp:0 dai:1 c:0 ooc:0
0x84,
// pdf:10 ef:1 erf:0 dtmf:0 acif:0 pcf:0 pef:0
0xc0,
// phdl:0000 0101 '0010' pts:000 mb:1 pts:0000 0000
0x05, 0x21, 0x00,
// pts:0000 000 mb:1 pts:0000 0000 pts:0000 000 mb:1
0x01, 0x00, 0x01,
// "data":0101
0x11
]));
ok(packet, 'parsed a packet');
equal('pes', packet.type, 'recognized a PES packet');
equal(0x1b, packet.streamType, 'tracked the stream_type');
equal(1, packet.data.byteLength, 'parsed a single data byte');
equal(0x11, packet.data[0], 'parsed the data');
equal(0, packet.pts, 'parsed the pts');
});
test('parses an elementary stream packet with a pts and dts', function() {
var packet;
parseStream.on('data', function(data) {
packet = data;
});
parseStream.programMapTable = {
0x11: 0x1b // pid 0x11 is h264 data
};
parseStream.push(new Uint8Array([
0x47, // sync byte
// tei:0 pusi:1 tp:0 pid:0 0000 0001 0001
0x40, 0x11,
// tsc:01 afc:01 cc:0000
0x50,
// pscp:0000 0000 0000 0000 0000 0001
0x00, 0x00, 0x01,
// sid:0000 0000 ppl:0000 0000 0000 1110
0x00, 0x00, 0x0e,
// 10 psc:00 pp:0 dai:1 c:0 ooc:0
0x84,
// pdf:11 ef:1 erf:0 dtmf:0 acif:0 pcf:0 pef:0
0xe0,
// phdl:0000 1010 '0011' pts:000 mb:1 pts:0000 0000
0x0a, 0x21, 0x00,
// pts:0000 000 mb:1 pts:0000 0000 pts:0000 100 mb:1
0x01, 0x00, 0x09,
// '0001' dts:000 mb:1 dts:0000 0000 dts:0000 000 mb:1
0x11, 0x00, 0x01,
// dts:0000 0000 dts:0000 010 mb:1
0x00, 0x05,
// "data":0101
0x11
]));
ok(packet, 'parsed a packet');
equal('pes', packet.type, 'recognized a PES packet');
equal(0x1b, packet.streamType, 'tracked the stream_type');
equal(1, packet.data.byteLength, 'parsed a single data byte');
equal(0x11, packet.data[0], 'parsed the data');
equal(4 / 90, packet.pts, 'parsed the pts');
equal(2 / 90, packet.dts, 'parsed the dts');
});
test('parses an elementary stream packet without a pts or dts', function() {
var packet;
parseStream.on('data', function(data) {
packet = data;
});
parseStream.programMapTable = {
0x11: 0x1b // pid 0x11 is h264 data
};
parseStream.push(new Uint8Array([
0x47, // sync byte
// tei:0 pusi:1 tp:0 pid:0 0000 0001 0001
0x40, 0x11,
// tsc:01 afc:01 cc:0000
0x50,
// pscp:0000 0000 0000 0000 0000 0001
0x00, 0x00, 0x01,
// sid:0000 0000 ppl:0000 0000 0000 0101
0x00, 0x00, 0x05,
// 10 psc:00 pp:0 dai:1 c:0 ooc:0
0x84,
// pdf:00 ef:1 erf:0 dtmf:0 acif:0 pcf:0 pef:0
0x20,
// phdl:0000 0000
0x00,
// "data":1010 1111 0000 0001
0xaf, 0x01
]));
ok(packet, 'parsed a packet');
equal('pes', packet.type, 'recognized a PES packet');
equal(0x1b, packet.streamType, 'tracked the stream_type');
equal(2, packet.data.byteLength, 'parsed two data bytes');
equal(0xaf, packet.data[0], 'parsed the first data byte');
equal(0x01, packet.data[1], 'parsed the second data byte');
ok(!packet.pts, 'did not parse a pts');
ok(!packet.dts, 'did not parse a dts');
});
module('MP4 Transmuxer', {
......