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.
Showing
2 changed files
with
49 additions
and
33 deletions
... | @@ -221,9 +221,9 @@ | ... | @@ -221,9 +221,9 @@ |
221 | patTableId, // :int | 221 | patTableId, // :int |
222 | patCurrentNextIndicator, // Boolean | 222 | patCurrentNextIndicator, // Boolean |
223 | patSectionLength, // :uint | 223 | patSectionLength, // :uint |
224 | patNumEntries, // :uint | 224 | programNumber, // :uint |
225 | patNumPrograms, // :uint | 225 | programPid, // :uint |
226 | patProgramOffset, // :uint | 226 | patEntriesEnd, // :uint |
227 | 227 | ||
228 | pesPacketSize, // :int, | 228 | pesPacketSize, // :int, |
229 | dataAlignmentIndicator, // :Boolean, | 229 | dataAlignmentIndicator, // :Boolean, |
... | @@ -296,6 +296,8 @@ | ... | @@ -296,6 +296,8 @@ |
296 | if (patCurrentNextIndicator) { | 296 | if (patCurrentNextIndicator) { |
297 | // section_length specifies the number of bytes following | 297 | // section_length specifies the number of bytes following |
298 | // its position to the end of this section | 298 | // its position to the end of this section |
299 | // section_length = rest of header + (n * entry length) + CRC | ||
300 | // = 5 + (n * 4) + 4 | ||
299 | patSectionLength = (data[offset + 1] & 0x0F) << 8 | data[offset + 2]; | 301 | patSectionLength = (data[offset + 1] & 0x0F) << 8 | data[offset + 2]; |
300 | // move past the rest of the PSI header to the first program | 302 | // move past the rest of the PSI header to the first program |
301 | // map table entry | 303 | // map table entry |
... | @@ -303,28 +305,22 @@ | ... | @@ -303,28 +305,22 @@ |
303 | 305 | ||
304 | // we don't handle streams with more than one program, so | 306 | // we don't handle streams with more than one program, so |
305 | // raise an exception if we encounter one | 307 | // raise an exception if we encounter one |
306 | // section_length = rest of header + (n * entry length) + CRC | 308 | patEntriesEnd = offset + (patSectionLength - 5 - 4); |
307 | // = 5 + (n * 4) + 4 | 309 | for (; offset < patEntriesEnd; offset += 4) { |
308 | patNumEntries = (patSectionLength - 5 - 4) / 4; | 310 | programNumber = (data[offset] << 8 | data[offset + 1]); |
309 | patNumPrograms = 0; | 311 | programPid = (data[offset + 2] & 0x1F) << 8 | data[offset + 3]; |
310 | patProgramOffset = offset; | 312 | // network PID program number equals 0 |
311 | for (var entryIndex = 0; entryIndex < patNumEntries; entryIndex++) { | 313 | // this is primarily an artifact of EBU DVB and can be ignored |
312 | // network PID program number equals 0 and can be ignored | 314 | if (programNumber === 0) { |
313 | if (((data[offset + 4*entryIndex]) << 8 | data[offset + 4*entryIndex + 1]) > 0) { | 315 | self.stream.networkPid = programPid; |
314 | patNumPrograms++; | 316 | } else if (self.stream.pmtPid === undefined) { |
315 | patProgramOffset = offset + 4*entryIndex; | 317 | // the Program Map Table (PMT) associates the underlying |
318 | // video and audio streams with a unique PID | ||
319 | self.stream.pmtPid = programPid; | ||
320 | } else if (self.stream.pmtPid !== programPid) { | ||
321 | throw new Error("TS has more that 1 program"); | ||
316 | } | 322 | } |
317 | } | 323 | } |
318 | if (patNumPrograms !== 1) { | ||
319 | throw new Error("TS has more that 1 program"); | ||
320 | } | ||
321 | else { | ||
322 | offset = patProgramOffset; | ||
323 | } | ||
324 | |||
325 | // the Program Map Table (PMT) associates the underlying | ||
326 | // video and audio streams with a unique PID | ||
327 | self.stream.pmtPid = (data[offset + 2] & 0x1F) << 8 | data[offset + 3]; | ||
328 | } | 324 | } |
329 | } else if (pid === self.stream.programMapTable[STREAM_TYPES.h264] || | 325 | } else if (pid === self.stream.programMapTable[STREAM_TYPES.h264] || |
330 | pid === self.stream.programMapTable[STREAM_TYPES.adts] || | 326 | pid === self.stream.programMapTable[STREAM_TYPES.adts] || |
... | @@ -477,6 +473,8 @@ | ... | @@ -477,6 +473,8 @@ |
477 | } | 473 | } |
478 | } | 474 | } |
479 | // We could test the CRC here to detect corruption with extra CPU cost | 475 | // We could test the CRC here to detect corruption with extra CPU cost |
476 | } else if (self.stream.networkPid === pid) { | ||
477 | // network information specific data (NIT) packet | ||
480 | } else if (0x0011 === pid) { | 478 | } else if (0x0011 === pid) { |
481 | // Service Description Table | 479 | // Service Description Table |
482 | } else if (0x1FFF === pid) { | 480 | } else if (0x1FFF === pid) { | ... | ... |
... | @@ -121,13 +121,26 @@ | ... | @@ -121,13 +121,26 @@ |
121 | makePat = function(options) { | 121 | makePat = function(options) { |
122 | var | 122 | var |
123 | result = [], | 123 | result = [], |
124 | programEntries = [], | ||
125 | sectionLength, | ||
124 | k; | 126 | k; |
125 | 127 | ||
128 | // build the program entries first | ||
129 | for (k in options.programs) { | ||
130 | // program_number | ||
131 | programEntries.push((k & 0xFF00) >>> 8); | ||
132 | programEntries.push(k & 0x00FF); | ||
133 | // reserved program_map_pid | ||
134 | programEntries.push((options.programs[k] & 0x1f00) >>> 8); | ||
135 | programEntries.push(options.programs[k] & 0xff); | ||
136 | } | ||
137 | sectionLength = programEntries.length + 5 + 4; | ||
138 | |||
126 | // table_id | 139 | // table_id |
127 | result.push(0x00); | 140 | result.push(0x00); |
128 | // section_syntax_indicator '0' reserved section_length | 141 | // section_syntax_indicator '0' reserved section_length |
129 | result.push(0x80); | 142 | result.push(0x80 | ((0x300 & sectionLength) >>> 8)); |
130 | result.push(0x0d); // section_length for one program | 143 | result.push(0xff & sectionLength); // section_length |
131 | // transport_stream_id | 144 | // transport_stream_id |
132 | result.push(0x00); | 145 | result.push(0x00); |
133 | result.push(0x00); | 146 | result.push(0x00); |
... | @@ -137,14 +150,8 @@ | ... | @@ -137,14 +150,8 @@ |
137 | result.push(0x00); | 150 | result.push(0x00); |
138 | // last_section_number | 151 | // last_section_number |
139 | result.push(0x00); | 152 | result.push(0x00); |
140 | for (k in options.programs) { | 153 | // program entries |
141 | // program_number | 154 | result = result.concat(programEntries); |
142 | result.push((k & 0xFF00) >>> 8); | ||
143 | result.push(k & 0x00FF); | ||
144 | // reserved program_map_pid | ||
145 | result.push((options.programs[k] & 0x1f00) >>> 8); | ||
146 | result.push(options.programs[k] & 0xff); | ||
147 | } | ||
148 | return result; | 155 | return result; |
149 | }; | 156 | }; |
150 | 157 | ||
... | @@ -211,6 +218,17 @@ | ... | @@ -211,6 +218,17 @@ |
211 | strictEqual(parser.stream.programMapTable[adtsType], 0x03, 'audio is PID 3'); | 218 | strictEqual(parser.stream.programMapTable[adtsType], 0x03, 'audio is PID 3'); |
212 | }); | 219 | }); |
213 | 220 | ||
221 | test('ignores network information specific data (NIT) in the PAT', function() { | ||
222 | parser.parseSegmentBinaryData(new Uint8Array(makePacket({ | ||
223 | programs: { | ||
224 | 0x01: [0x01], | ||
225 | 0x00: [0x00] // a NIT has a reserved PID of 0x00 | ||
226 | } | ||
227 | }))); | ||
228 | |||
229 | ok(true, 'did not throw when a NIT is encountered'); | ||
230 | }); | ||
231 | |||
214 | test('recognizes metadata streams', function() { | 232 | test('recognizes metadata streams', function() { |
215 | parser.parseSegmentBinaryData(new Uint8Array(makePacket({ | 233 | parser.parseSegmentBinaryData(new Uint8Array(makePacket({ |
216 | programs: { | 234 | programs: { | ... | ... |
-
Please register or sign in to post a comment