Close off all FLV tags at the end of a segment
The segment parser allows fragmentary input to the muxing process so it's not always clear when a tag should be finalized at the end of the input. By calling segmentParser.flushTags(), the parser is instructed to wrap up whatever input it currently has buffered into an FLV tag. Before this change, the last tag of the video stream would be buffered in the parser, waiting for additional input (i.e. another segment download) to flush it out. When you seeked within a segment, that last tag would have a timestamp greater than your seek point and we were assuming that timestamp values were sorted in ascending order. We would see the timestamp value greater than the desired seek location and start feeding tags into the media source, which resulted in the segment appearing to restart. Now, we close off any tags that are buffered at the end of a segment so the inter-segment seeking routine can assume that tags are delivered in the order of playback.
Showing
3 changed files
with
14 additions
and
20 deletions
... | @@ -292,7 +292,7 @@ | ... | @@ -292,7 +292,7 @@ |
292 | 292 | ||
293 | //(pts:uint, dts:uint, dataAligned:Boolean):void | 293 | //(pts:uint, dts:uint, dataAligned:Boolean):void |
294 | this.setNextTimeStamp = function(pts, dts, dataAligned) { | 294 | this.setNextTimeStamp = function(pts, dts, dataAligned) { |
295 | if (0>pts_delta) { | 295 | if (pts_delta < 0) { |
296 | // We assume the very first pts is less than 0x8FFFFFFF (max signed | 296 | // We assume the very first pts is less than 0x8FFFFFFF (max signed |
297 | // int32) | 297 | // int32) |
298 | pts_delta = pts; | 298 | pts_delta = pts; |
... | @@ -310,7 +310,7 @@ | ... | @@ -310,7 +310,7 @@ |
310 | 310 | ||
311 | this.finishFrame = function() { | 311 | this.finishFrame = function() { |
312 | if (h264Frame) { | 312 | if (h264Frame) { |
313 | // Push SPS before EVERY IDR frame fo seeking | 313 | // Push SPS before EVERY IDR frame for seeking |
314 | if (newExtraData.extraDataExists()) { | 314 | if (newExtraData.extraDataExists()) { |
315 | oldExtraData = newExtraData; | 315 | oldExtraData = newExtraData; |
316 | newExtraData = new H264ExtraData(); | 316 | newExtraData = new H264ExtraData(); | ... | ... |
... | @@ -358,9 +358,6 @@ | ... | @@ -358,9 +358,6 @@ |
358 | offset += pesHeaderLength; | 358 | offset += pesHeaderLength; |
359 | 359 | ||
360 | if (pid === self.stream.programMapTable[STREAM_TYPES.h264]) { | 360 | if (pid === self.stream.programMapTable[STREAM_TYPES.h264]) { |
361 | // Stash this frame for future use. | ||
362 | // console.assert(videoFrames.length < 3); | ||
363 | |||
364 | h264Stream.setNextTimeStamp(pts, | 361 | h264Stream.setNextTimeStamp(pts, |
365 | dts, | 362 | dts, |
366 | dataAlignmentIndicator); | 363 | dataAlignmentIndicator); | ... | ... |
... | @@ -293,8 +293,8 @@ var | ... | @@ -293,8 +293,8 @@ var |
293 | 293 | ||
294 | sortedPlaylists.sort(playlistBandwidth); | 294 | sortedPlaylists.sort(playlistBandwidth); |
295 | 295 | ||
296 | // map playlist options by bandwidth and select | 296 | // filter out any variant that has greater effective bitrate |
297 | // best variant as appropriate | 297 | // than the current estimated bandwidth |
298 | while (i--) { | 298 | while (i--) { |
299 | variant = sortedPlaylists[i]; | 299 | variant = sortedPlaylists[i]; |
300 | 300 | ||
... | @@ -305,39 +305,36 @@ var | ... | @@ -305,39 +305,36 @@ var |
305 | 305 | ||
306 | effectiveBitrate = variant.attributes.BANDWIDTH * bandwidthVariance; | 306 | effectiveBitrate = variant.attributes.BANDWIDTH * bandwidthVariance; |
307 | 307 | ||
308 | // since the playlists are sorted in ascending order by bandwidth, the | ||
309 | // current variant is the best as long as its effective bitrate is | ||
310 | // below the current bandwidth estimate | ||
311 | // NOTE - only set once | ||
312 | if (effectiveBitrate < player.hls.bandwidth) { | 308 | if (effectiveBitrate < player.hls.bandwidth) { |
313 | bandwidthPlaylists.push(variant); | 309 | bandwidthPlaylists.push(variant); |
310 | |||
311 | // since the playlists are sorted in ascending order by | ||
312 | // bandwidth, the first viable variant is the best | ||
314 | if (!bandwidthBestVariant) { | 313 | if (!bandwidthBestVariant) { |
315 | bandwidthBestVariant = variant; | 314 | bandwidthBestVariant = variant; |
316 | } | 315 | } |
317 | } | 316 | } |
318 | } | 317 | } |
319 | 318 | ||
320 | // set index to the available bandwidth mapped renditions | ||
321 | i = bandwidthPlaylists.length; | 319 | i = bandwidthPlaylists.length; |
322 | 320 | ||
323 | // sort those by resolution [currently widths] | 321 | // sort variants by resolution |
324 | bandwidthPlaylists.sort(playlistResolution); | 322 | bandwidthPlaylists.sort(playlistResolution); |
325 | 323 | ||
326 | // iterate through bandwidth related playlists and find | 324 | // iterate through the bandwidth-filtered playlists and find |
327 | // best rendition by player dimension | 325 | // best rendition by player dimension |
328 | |||
329 | // Tests | ||
330 | // Seeking - find if you've seeked correctly? | ||
331 | // SelectPlaylist - | ||
332 | while (i--) { | 326 | while (i--) { |
333 | variant = bandwidthPlaylists[i]; | 327 | variant = bandwidthPlaylists[i]; |
334 | 328 | ||
335 | // ignored playlists without resolution information | 329 | // ignore playlists without resolution information |
336 | if (!variant.attributes || !variant.attributes.RESOLUTION || | 330 | if (!variant.attributes || !variant.attributes.RESOLUTION || |
337 | !variant.attributes.RESOLUTION.width || !variant.attributes.RESOLUTION.height) { | 331 | !variant.attributes.RESOLUTION.width || !variant.attributes.RESOLUTION.height) { |
338 | continue; | 332 | continue; |
339 | } | 333 | } |
340 | 334 | ||
335 | // since the playlists are sorted, the first variant that has | ||
336 | // dimensions less than or equal to the player size is the | ||
337 | // best | ||
341 | if (variant.attributes.RESOLUTION.width <= player.width() && | 338 | if (variant.attributes.RESOLUTION.width <= player.width() && |
342 | variant.attributes.RESOLUTION.height <= player.height()) { | 339 | variant.attributes.RESOLUTION.height <= player.height()) { |
343 | resolutionBestVariant = variant; | 340 | resolutionBestVariant = variant; |
... | @@ -526,13 +523,13 @@ var | ... | @@ -526,13 +523,13 @@ var |
526 | 523 | ||
527 | // transmux the segment data from MP2T to FLV | 524 | // transmux the segment data from MP2T to FLV |
528 | segmentParser.parseSegmentBinaryData(new Uint8Array(this.response)); | 525 | segmentParser.parseSegmentBinaryData(new Uint8Array(this.response)); |
526 | segmentParser.flushTags(); | ||
529 | 527 | ||
530 | // if we're refilling the buffer after a seek, scan through the muxed | 528 | // if we're refilling the buffer after a seek, scan through the muxed |
531 | // FLV tags until we find the one that is closest to the desired | 529 | // FLV tags until we find the one that is closest to the desired |
532 | // playback time | 530 | // playback time |
533 | if (offset !== undefined && typeof offset === "number") { | 531 | if (offset !== undefined && typeof offset === "number") { |
534 | while (segmentParser.getTags()[0].pts < offset) { | 532 | while (segmentParser.getTags()[0].pts < offset) { |
535 | // we're seeking past this tag, so ignore it | ||
536 | segmentParser.getNextTag(); | 533 | segmentParser.getNextTag(); |
537 | } | 534 | } |
538 | } | 535 | } | ... | ... |
-
Please register or sign in to post a comment