dbeb3eac by David LaPalomento

Write out audio metadata frequently. Fixes #169.

If the source was switched among variant playlists that have different audio sampling rates and the initial audio metadata tag skipped, audio would dissapear. Instead, emit an audio meta tag at least once per second so that the decoder can get back on track after a change. Fix typo that caused audio DTS not to be written out properly.
1 parent 5662e402
...@@ -23,6 +23,7 @@ window.videojs.Hls.AacStream = function() { ...@@ -23,6 +23,7 @@ window.videojs.Hls.AacStream = function() {
23 pts_offset, // :int 23 pts_offset, // :int
24 state, // :uint 24 state, // :uint
25 pes_length, // :int 25 pes_length, // :int
26 lastMetaPts,
26 27
27 adtsProtectionAbsent, // :Boolean 28 adtsProtectionAbsent, // :Boolean
28 adtsObjectType, // :int 29 adtsObjectType, // :int
...@@ -43,6 +44,11 @@ window.videojs.Hls.AacStream = function() { ...@@ -43,6 +44,11 @@ window.videojs.Hls.AacStream = function() {
43 // on the first invocation, capture the starting PTS value 44 // on the first invocation, capture the starting PTS value
44 pts_offset = pts; 45 pts_offset = pts;
45 46
47 // keep track of the last time a metadata tag was written out
48 // set the initial value so metadata will be generated before any
49 // payload data
50 lastMetaPts = pts - 1000;
51
46 // on subsequent invocations, calculate the PTS based on the starting offset 52 // on subsequent invocations, calculate the PTS based on the starting offset
47 this.setNextTimeStamp = function(pts, pes_size, dataAligned) { 53 this.setNextTimeStamp = function(pts, pes_size, dataAligned) {
48 next_pts = pts - pts_offset; 54 next_pts = pts - pts_offset;
...@@ -157,7 +163,11 @@ window.videojs.Hls.AacStream = function() { ...@@ -157,7 +163,11 @@ window.videojs.Hls.AacStream = function() {
157 newExtraData = (adtsObjectType << 11) | 163 newExtraData = (adtsObjectType << 11) |
158 (adtsSampleingIndex << 7) | 164 (adtsSampleingIndex << 7) |
159 (adtsChanelConfig << 3); 165 (adtsChanelConfig << 3);
160 if (newExtraData !== extraData) { 166
167 // write out metadata tags every 1 second so that the decoder
168 // is re-initialized quickly after seeking into a different
169 // audio configuration
170 if (newExtraData !== extraData || next_pts - lastMetaPts >= 1000) {
161 aacFrame = new FlvTag(FlvTag.METADATA_TAG); 171 aacFrame = new FlvTag(FlvTag.METADATA_TAG);
162 aacFrame.pts = next_pts; 172 aacFrame.pts = next_pts;
163 aacFrame.dts = next_pts; 173 aacFrame.dts = next_pts;
...@@ -173,16 +183,19 @@ window.videojs.Hls.AacStream = function() { ...@@ -173,16 +183,19 @@ window.videojs.Hls.AacStream = function() {
173 183
174 extraData = newExtraData; 184 extraData = newExtraData;
175 aacFrame = new FlvTag(FlvTag.AUDIO_TAG, true); 185 aacFrame = new FlvTag(FlvTag.AUDIO_TAG, true);
176 aacFrame.pts = aacFrame.dts;
177 // For audio, DTS is always the same as PTS. We want to set the DTS 186 // For audio, DTS is always the same as PTS. We want to set the DTS
178 // however so we can compare with video DTS to determine approximate 187 // however so we can compare with video DTS to determine approximate
179 // packet order 188 // packet order
180 aacFrame.pts = next_pts; 189 aacFrame.pts = next_pts;
190 aacFrame.dts = aacFrame.pts;
191
181 aacFrame.view.setUint16(aacFrame.position, newExtraData); 192 aacFrame.view.setUint16(aacFrame.position, newExtraData);
182 aacFrame.position += 2; 193 aacFrame.position += 2;
183 aacFrame.length = Math.max(aacFrame.length, aacFrame.position); 194 aacFrame.length = Math.max(aacFrame.length, aacFrame.position);
184 195
185 this.tags.push(aacFrame); 196 this.tags.push(aacFrame);
197
198 lastMetaPts = next_pts;
186 } 199 }
187 200
188 // Skip the checksum if there is one 201 // Skip the checksum if there is one
......
...@@ -341,14 +341,22 @@ ...@@ -341,14 +341,22 @@
341 currentPts = tag.pts; 341 currentPts = tag.pts;
342 342
343 // generic flv headers 343 // generic flv headers
344 ok(type === 8 || type === 9 || type === 18, 344 switch (type) {
345 'the type field specifies audio, video or script'); 345 case 8: ok(true, 'the type is audio');
346 break;
347 case 9: ok(true, 'the type is video');
348 break;
349 case 18: ok(true, 'the type is script');
350 break;
351 default: ok(false, 'the type (' + type + ') is unrecognized');
352 }
346 353
347 byte = (tag.view.getUint32(1) & 0xFFFFFF00) >>> 8; 354 byte = (tag.view.getUint32(1) & 0xFFFFFF00) >>> 8;
348 equal(tag.bytes.byteLength - 11 - 4, byte, 'the size field is correct'); 355 equal(tag.bytes.byteLength - 11 - 4, byte, 'the size field is correct');
349 356
350 byte = tag.view.getUint32(5) & 0xFFFFFF00; 357 byte = tag.view.getUint32(5) & 0xFFFFFF00;
351 ok(byte >= lastTime, 'the timestamp for the tag is greater than zero'); 358 ok(byte >= lastTime,
359 'timestamp is increasing. last pts: ' + lastTime + ' this pts: ' + byte);
352 lastTime = byte; 360 lastTime = byte;
353 361
354 // tag type-specific headers 362 // tag type-specific headers
......