1c56b76c by David LaPalomento

Add support for program info descriptors

We were assuming that the program_info_descriptors field in the program mapping table was always of zero length and would end up misaligned reading the table entries if that metadata was present. Also added helper functions for testing to generate mp2t packets.
1 parent 8b4d830c
...@@ -26,6 +26,9 @@ ...@@ -26,6 +26,9 @@
26 0x46, 0x4c, 0x56, 0x01, 0x05, 0x00, 0x00, 0x00, 26 0x46, 0x4c, 0x56, 0x01, 0x05, 0x00, 0x00, 0x00,
27 0x09, 0x00, 0x00, 0x00, 0x00 27 0x09, 0x00, 0x00, 0x00, 0x00
28 ], 28 ],
29
30 extend = videojs.util.mergeOptions,
31
29 testAudioTag, 32 testAudioTag,
30 testVideoTag, 33 testVideoTag,
31 testScriptTag, 34 testScriptTag,
...@@ -51,6 +54,144 @@ ...@@ -51,6 +54,144 @@
51 deepEqual(expectedHeader, header, 'the rest of the header is correct'); 54 deepEqual(expectedHeader, header, 'the rest of the header is correct');
52 }); 55 });
53 56
57 test('parses PMTs with program descriptors', function() {
58 var
59 makePmt = function(options) {
60 var
61 result = [],
62 entryCount = 0,
63 k,
64 sectionLength;
65 for (k in options.pids) {
66 entryCount++;
67 }
68 // table_id
69 result.push(0x02);
70 // section_syntax_indicator '0' reserved section_length
71 // 13 + (program_info_length) + (n * 5 + ES_info_length[n])
72 sectionLength = 13 + (5 * entryCount) + 17;
73 result.push(0x80 | (0xF00 & sectionLength >>> 8));
74 result.push(sectionLength & 0xFF);
75 // program_number
76 result.push(0x00);
77 result.push(0x01);
78 // reserved version_number current_next_indicator
79 result.push(0x01);
80 // section_number
81 result.push(0x00);
82 // last_section_number
83 result.push(0x00);
84 // reserved PCR_PID
85 result.push(0xe1);
86 result.push(0x00);
87 // reserved program_info_length
88 result.push(0xf0);
89 result.push(0x11); // hard-coded 17 byte descriptor
90 // program descriptors
91 result = result.concat([
92 0x25, 0x0f, 0xff, 0xff,
93 0x49, 0x44, 0x33, 0x20,
94 0xff, 0x49, 0x44, 0x33,
95 0x20, 0x00, 0x1f, 0x00,
96 0x01
97 ]);
98 for (k in options.pids) {
99 // stream_type
100 result.push(options.pids[k]);
101 // reserved elementary_PID
102 result.push(0xe0 | (k & 0x1f00) >>> 8);
103 result.push(k & 0xff);
104 // reserved ES_info_length
105 result.push(0xf0);
106 result.push(0x00); // ES_info_length = 0
107 }
108 // CRC_32
109 result.push([0x00, 0x00, 0x00, 0x00]); // invalid CRC but we don't check it
110 return result;
111 },
112 makePat = function(options) {
113 var
114 result = [],
115 k;
116 // table_id
117 result.push(0x00);
118 // section_syntax_indicator '0' reserved section_length
119 result.push(0x80);
120 result.push(0x0d); // section_length for one program
121 // transport_stream_id
122 result.push(0x00);
123 result.push(0x00);
124 // reserved version_number current_next_indicator
125 result.push(0x01); // current_next_indicator is 1
126 // section_number
127 result.push(0x00);
128 // last_section_number
129 result.push(0x00);
130 for (k in options.programs) {
131 // program_number
132 result.push((k & 0xFF00) >>> 8);
133 result.push(k & 0x00FF);
134 // reserved program_map_pid
135 result.push((options.programs[k] & 0x1f00) >>> 8);
136 result.push(options.programs[k] & 0xff);
137 }
138 return result;
139 },
140 makePsi = function(options) {
141 var result = [];
142
143 // pointer_field
144 if (options.payloadUnitStartIndicator) {
145 result.push(0x00);
146 }
147 if (options.programs) {
148 return result.concat(makePat(options));
149 }
150 return result.concat(makePmt(options));
151 },
152 makePacket = function(options) {
153 var
154 result = [],
155 settings = extend({
156 payloadUnitStartIndicator: true,
157 pid: 0x00
158 }, options),
159 pid;
160
161 // header
162 // sync_byte
163 result.push(0x47);
164 // transport_error_indicator payload_unit_start_indicator transport_priority PID
165 result.push((settings.pid & 0x1f) << 8 | 0x40);
166 result.push(settings.pid & 0xff);
167 // transport_scrambling_control adaptation_field_control continuity_counter
168 result.push(0x10);
169 result = result.concat(makePsi(settings));
170
171 // ensure the resulting packet is the correct size
172 result.length = videojs.hls.SegmentParser.MP2T_PACKET_LENGTH;
173 return result;
174 },
175 h264Type = videojs.hls.SegmentParser.STREAM_TYPES.h264,
176 adtsType = videojs.hls.SegmentParser.STREAM_TYPES.adts;
177
178 parser.parseSegmentBinaryData(new Uint8Array(makePacket({
179 programs: {
180 0x01: [0x01]
181 }
182 }).concat(makePacket({
183 pid: 0x01,
184 pids: {
185 0x02: h264Type, // h264 video
186 0x03: adtsType // adts audio
187 }
188 }))));
189
190 strictEqual(parser.stream.pmtPid, 0x01, 'PMT PID is 1');
191 strictEqual(parser.stream.programMapTable[h264Type], 0x02, 'video is PID 2');
192 strictEqual(parser.stream.programMapTable[adtsType], 0x03, 'audio is PID 3');
193 });
194
54 test('parses the first bipbop segment', function() { 195 test('parses the first bipbop segment', function() {
55 parser.parseSegmentBinaryData(window.bcSegment); 196 parser.parseSegmentBinaryData(window.bcSegment);
56 197
......