From ee7d14199cd70541d14dd41feeb7dc0d80f92d11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jernej=20Fija=C4=8Dko?= Date: Tue, 26 May 2015 14:40:22 +0200 Subject: [PATCH 1/3] Allow playback of content with network PID entries inside PAT --- src/segment-parser.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/segment-parser.js b/src/segment-parser.js index 750e9ce25..a40059e9e 100644 --- a/src/segment-parser.js +++ b/src/segment-parser.js @@ -221,6 +221,9 @@ patTableId, // :int patCurrentNextIndicator, // Boolean patSectionLength, // :uint + patNumEntries, // :uint + patNumPrograms, // :uint + patProgramOffset, // :uint pesPacketSize, // :int, dataAlignmentIndicator, // :Boolean, @@ -302,9 +305,22 @@ // raise an exception if we encounter one // section_length = rest of header + (n * entry length) + CRC // = 5 + (n * 4) + 4 - if ((patSectionLength - 5 - 4) / 4 !== 1) { + patNumEntries = (patSectionLength - 5 - 4) / 4; + patNumPrograms = 0; + patProgramOffset = offset; + for (var entryIndex = 0; entryIndex < patNumEntries; entryIndex++) { + // network PID program number equals 0 and can be ignored + if (((data[offset + 4*entryIndex]) << 8 | data[offset + 4*entryIndex + 1]) > 0) { + patNumPrograms++; + patProgramOffset = offset + 4*entryIndex; + } + } + if (patNumPrograms !== 1) { throw new Error("TS has more that 1 program"); } + else { + offset = patProgramOffset; + } // the Program Map Table (PMT) associates the underlying // video and audio streams with a unique PID From c962fb7c6d757e2d06e94a8fc614495c317e6e04 Mon Sep 17 00:00:00 2001 From: David LaPalomento Date: Wed, 27 May 2015 16:46:21 -0400 Subject: [PATCH 2/3] Capture NIT so we generate less warnings When a NIT was present, we would warn about unrecognized PIDs every time it was encountered. Capture the PID value so we can silently ignore it. Add a test case for NIT parsing. --- src/segment-parser.js | 44 ++++++++++++++++++++---------------------- test/segment-parser.js | 38 ++++++++++++++++++++++++++---------- 2 files changed, 49 insertions(+), 33 deletions(-) diff --git a/src/segment-parser.js b/src/segment-parser.js index a40059e9e..6811304e3 100644 --- a/src/segment-parser.js +++ b/src/segment-parser.js @@ -221,9 +221,9 @@ patTableId, // :int patCurrentNextIndicator, // Boolean patSectionLength, // :uint - patNumEntries, // :uint - patNumPrograms, // :uint - patProgramOffset, // :uint + programNumber, // :uint + programPid, // :uint + patEntriesEnd, // :uint pesPacketSize, // :int, dataAlignmentIndicator, // :Boolean, @@ -296,6 +296,8 @@ if (patCurrentNextIndicator) { // section_length specifies the number of bytes following // its position to the end of this section + // section_length = rest of header + (n * entry length) + CRC + // = 5 + (n * 4) + 4 patSectionLength = (data[offset + 1] & 0x0F) << 8 | data[offset + 2]; // move past the rest of the PSI header to the first program // map table entry @@ -303,28 +305,22 @@ // we don't handle streams with more than one program, so // raise an exception if we encounter one - // section_length = rest of header + (n * entry length) + CRC - // = 5 + (n * 4) + 4 - patNumEntries = (patSectionLength - 5 - 4) / 4; - patNumPrograms = 0; - patProgramOffset = offset; - for (var entryIndex = 0; entryIndex < patNumEntries; entryIndex++) { - // network PID program number equals 0 and can be ignored - if (((data[offset + 4*entryIndex]) << 8 | data[offset + 4*entryIndex + 1]) > 0) { - patNumPrograms++; - patProgramOffset = offset + 4*entryIndex; + patEntriesEnd = offset + (patSectionLength - 5 - 4); + for (; offset < patEntriesEnd; offset += 4) { + programNumber = (data[offset] << 8 | data[offset + 1]); + programPid = (data[offset + 2] & 0x1F) << 8 | data[offset + 3]; + // network PID program number equals 0 + // this is primarily an artifact of EBU DVB and can be ignored + if (programNumber === 0) { + self.stream.networkPid = programPid; + } else if (self.stream.pmtPid === undefined) { + // the Program Map Table (PMT) associates the underlying + // video and audio streams with a unique PID + self.stream.pmtPid = programPid; + } else if (self.stream.pmtPid !== programPid) { + throw new Error("TS has more that 1 program"); } } - if (patNumPrograms !== 1) { - throw new Error("TS has more that 1 program"); - } - else { - offset = patProgramOffset; - } - - // the Program Map Table (PMT) associates the underlying - // video and audio streams with a unique PID - self.stream.pmtPid = (data[offset + 2] & 0x1F) << 8 | data[offset + 3]; } } else if (pid === self.stream.programMapTable[STREAM_TYPES.h264] || pid === self.stream.programMapTable[STREAM_TYPES.adts] || @@ -477,6 +473,8 @@ } } // We could test the CRC here to detect corruption with extra CPU cost + } else if (self.stream.networkPid === pid) { + // network information specific data (NIT) packet } else if (0x0011 === pid) { // Service Description Table } else if (0x1FFF === pid) { diff --git a/test/segment-parser.js b/test/segment-parser.js index 91e7294b3..d066f1012 100644 --- a/test/segment-parser.js +++ b/test/segment-parser.js @@ -121,13 +121,26 @@ makePat = function(options) { var result = [], + programEntries = [], + sectionLength, k; + // build the program entries first + for (k in options.programs) { + // program_number + programEntries.push((k & 0xFF00) >>> 8); + programEntries.push(k & 0x00FF); + // reserved program_map_pid + programEntries.push((options.programs[k] & 0x1f00) >>> 8); + programEntries.push(options.programs[k] & 0xff); + } + sectionLength = programEntries.length + 5 + 4; + // table_id result.push(0x00); // section_syntax_indicator '0' reserved section_length - result.push(0x80); - result.push(0x0d); // section_length for one program + result.push(0x80 | ((0x300 & sectionLength) >>> 8)); + result.push(0xff & sectionLength); // section_length // transport_stream_id result.push(0x00); result.push(0x00); @@ -137,14 +150,8 @@ result.push(0x00); // last_section_number result.push(0x00); - for (k in options.programs) { - // program_number - result.push((k & 0xFF00) >>> 8); - result.push(k & 0x00FF); - // reserved program_map_pid - result.push((options.programs[k] & 0x1f00) >>> 8); - result.push(options.programs[k] & 0xff); - } + // program entries + result = result.concat(programEntries); return result; }; @@ -211,6 +218,17 @@ strictEqual(parser.stream.programMapTable[adtsType], 0x03, 'audio is PID 3'); }); + test('ignores network information specific data (NIT) in the PAT', function() { + parser.parseSegmentBinaryData(new Uint8Array(makePacket({ + programs: { + 0x01: [0x01], + 0x00: [0x00] // a NIT has a reserved PID of 0x00 + } + }))); + + ok(true, 'did not throw when a NIT is encountered'); + }); + test('recognizes metadata streams', function() { parser.parseSegmentBinaryData(new Uint8Array(makePacket({ programs: { From 0c7d060362fae6d88c4175c0cc55aaedebb66cbb Mon Sep 17 00:00:00 2001 From: David LaPalomento Date: Wed, 27 May 2015 17:09:37 -0400 Subject: [PATCH 3/3] Don't warn when PCR pids are encountered We don't need a PCR stream for a program to keep synchronized but it's not a problem if one is included. Parse out the PCR PID when it's specified and make sure the warning message isn't output when they are encountered. --- src/segment-parser.js | 7 +++++++ test/segment-parser.js | 22 +++++++++++++++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/segment-parser.js b/src/segment-parser.js index 6811304e3..58ff64853 100644 --- a/src/segment-parser.js +++ b/src/segment-parser.js @@ -437,6 +437,9 @@ // rest of header + CRC = 9 + 4 pmtSectionLength -= 13; + // capture the PID of PCR packets so we can ignore them if we see any + self.stream.programMapTable.pcrPid = (data[offset + 8] & 0x1f) << 8 | data[offset + 9]; + // align offset to the first entry in the PMT offset += 12 + pmtProgramDescriptorsLength; @@ -479,6 +482,10 @@ // Service Description Table } else if (0x1FFF === pid) { // NULL packet + } else if (self.stream.programMapTable.pcrPid) { + // program clock reference (PCR) PID for the primary program + // PTS values are sufficient to synchronize playback for us so + // we can safely ignore these } else { videojs.log("Unknown PID parsing TS packet: " + pid); } diff --git a/test/segment-parser.js b/test/segment-parser.js index d066f1012..4d518de09 100644 --- a/test/segment-parser.js +++ b/test/segment-parser.js @@ -64,12 +64,15 @@ makePmt = function(options) { var result = [], + pcr = options.pcr || 0, entryCount = 0, k, sectionLength; for (k in options.pids) { - entryCount++; + if (k !== 'pcr') { + entryCount++; + } } // table_id result.push(0x02); @@ -88,8 +91,8 @@ // last_section_number result.push(0x00); // reserved PCR_PID - result.push(0xe1); - result.push(0x00); + result.push(0xe0 | (pcr & (0x1f << 8))); + result.push(pcr & 0xff); // reserved program_info_length result.push(0xf0); result.push(0x11); // hard-coded 17 byte descriptor @@ -229,6 +232,19 @@ ok(true, 'did not throw when a NIT is encountered'); }); + test('ignores packets with PCR pids', function() { + parser.parseSegmentBinaryData(new Uint8Array(makePacket({ + programs: { + 0x01: [0x01] + } + }).concat(makePacket({ + pid: 0x01, + pcr: 0x02 + })))); + + equal(parser.stream.programMapTable.pcrPid, 0x02, 'parsed the PCR pid'); + }); + test('recognizes metadata streams', function() { parser.parseSegmentBinaryData(new Uint8Array(makePacket({ programs: {