Be more forgiving about incompatibilities
Allow h.264 codecs to mingle with one another. Only exclude variants based on audio if one or the other is using HE-AAC.
Showing
2 changed files
with
99 additions
and
7 deletions
... | @@ -294,6 +294,27 @@ videojs.Hls.bufferedAdditions_ = function(original, update) { | ... | @@ -294,6 +294,27 @@ videojs.Hls.bufferedAdditions_ = function(original, update) { |
294 | return result; | 294 | return result; |
295 | }; | 295 | }; |
296 | 296 | ||
297 | |||
298 | var parseCodecs = function(codecs) { | ||
299 | var result = { | ||
300 | codecCount: 0, | ||
301 | videoCodec: null, | ||
302 | audioProfile: null | ||
303 | }; | ||
304 | |||
305 | result.codecCount = codecs.split(',').length; | ||
306 | result.codecCount = result.codecCount || 2; | ||
307 | |||
308 | // parse the video codec but ignore the version | ||
309 | result.videoCodec = /(^|\s|,)+(avc1)[^ ,]*/i.exec(codecs); | ||
310 | result.videoCodec = result.videoCodec && result.videoCodec[2]; | ||
311 | |||
312 | // parse the last field of the audio codec | ||
313 | result.audioProfile = /(^|\s|,)+mp4a.\d+\.(\d+)/i.exec(codecs); | ||
314 | result.audioProfile = result.audioProfile && result.audioProfile[2]; | ||
315 | |||
316 | return result; | ||
317 | }; | ||
297 | /** | 318 | /** |
298 | * Blacklist playlists that are known to be codec or | 319 | * Blacklist playlists that are known to be codec or |
299 | * stream-incompatible with the SourceBuffer configuration. For | 320 | * stream-incompatible with the SourceBuffer configuration. For |
... | @@ -307,19 +328,46 @@ videojs.Hls.bufferedAdditions_ = function(original, update) { | ... | @@ -307,19 +328,46 @@ videojs.Hls.bufferedAdditions_ = function(original, update) { |
307 | * will be excluded from the default playlist selection algorithm | 328 | * will be excluded from the default playlist selection algorithm |
308 | * indefinitely. | 329 | * indefinitely. |
309 | */ | 330 | */ |
310 | videojs.HlsHandler.prototype.excludeIncompatibleStreams_ = function(media) { | 331 | videojs.HlsHandler.prototype.excludeIncompatibleVariants_ = function(media) { |
311 | var master = this.playlists.master, codecs = ''; | 332 | var |
333 | master = this.playlists.master, | ||
334 | codecCount = 2, | ||
335 | videoCodec = null, | ||
336 | audioProfile = null, | ||
337 | codecs; | ||
312 | 338 | ||
313 | if (media.attributes && media.attributes.CODECS) { | 339 | if (media.attributes && media.attributes.CODECS) { |
314 | codecs = media.attributes.CODECS; | 340 | codecs = parseCodecs(media.attributes.CODECS); |
341 | videoCodec = codecs.videoCodec; | ||
342 | audioProfile = codecs.audioProfile; | ||
343 | codecCount = codecs.codecCount; | ||
315 | } | 344 | } |
316 | master.playlists.forEach(function(variant) { | 345 | master.playlists.forEach(function(variant) { |
317 | var variantCodecs = ''; | 346 | var variantCodecs = { |
347 | codecCount: 2, | ||
348 | videoCodec: null, | ||
349 | audioProfile: null | ||
350 | }; | ||
351 | |||
318 | if (variant.attributes && variant.attributes.CODECS) { | 352 | if (variant.attributes && variant.attributes.CODECS) { |
319 | variantCodecs = variant.attributes.CODECS; | 353 | variantCodecs = parseCodecs(variant.attributes.CODECS); |
354 | } | ||
355 | |||
356 | // if the streams differ in the presence or absence of audio or | ||
357 | // video, they are incompatible | ||
358 | if (variantCodecs.codecCount !== codecCount) { | ||
359 | variant.excludeUntil = Infinity; | ||
320 | } | 360 | } |
321 | 361 | ||
322 | if (variantCodecs !== codecs) { | 362 | // if h.264 is specified on the current playlist, some flavor of |
363 | // it must be specified on all compatible variants | ||
364 | if (variantCodecs.videoCodec !== videoCodec) { | ||
365 | variant.excludeUntil = Infinity; | ||
366 | } | ||
367 | // HE-AAC ("mp4a.40.5") is incompatible with all other versions of | ||
368 | // AAC audio in Chrome 46. Don't mix the two. | ||
369 | if ((variantCodecs.audioProfile === '5' && audioProfile !== '5') || | ||
370 | (audioProfile === '5' && variantCodecs.audioProfile !== '5')) { | ||
323 | variant.excludeUntil = Infinity; | 371 | variant.excludeUntil = Infinity; |
324 | } | 372 | } |
325 | }); | 373 | }); |
... | @@ -344,7 +392,7 @@ videojs.HlsHandler.prototype.setupSourceBuffer_ = function() { | ... | @@ -344,7 +392,7 @@ videojs.HlsHandler.prototype.setupSourceBuffer_ = function() { |
344 | 392 | ||
345 | // exclude any incompatible variant streams from future playlist | 393 | // exclude any incompatible variant streams from future playlist |
346 | // selection | 394 | // selection |
347 | this.excludeIncompatibleStreams_(media); | 395 | this.excludeIncompatibleVariants_(media); |
348 | 396 | ||
349 | // transition the sourcebuffer to the ended state if we've hit the end of | 397 | // transition the sourcebuffer to the ended state if we've hit the end of |
350 | // the playlist | 398 | // the playlist | ... | ... |
... | @@ -1171,6 +1171,50 @@ test('blacklists switching from video-only playlists to video+audio', function() | ... | @@ -1171,6 +1171,50 @@ test('blacklists switching from video-only playlists to video+audio', function() |
1171 | equal(videoAudioPlaylist.excludeUntil, Infinity, 'excluded incompatible playlist'); | 1171 | equal(videoAudioPlaylist.excludeUntil, Infinity, 'excluded incompatible playlist'); |
1172 | }); | 1172 | }); |
1173 | 1173 | ||
1174 | test('does not blacklist compatible H.264 codec strings', function() { | ||
1175 | var master; | ||
1176 | player.src({ | ||
1177 | src: 'manifest/master.m3u8', | ||
1178 | type: 'application/vnd.apple.mpegurl' | ||
1179 | }); | ||
1180 | openMediaSource(player); | ||
1181 | |||
1182 | player.tech_.hls.bandwidth = 1; | ||
1183 | requests.shift().respond(200, null, | ||
1184 | '#EXTM3U\n' + | ||
1185 | '#EXT-X-STREAM-INF:BANDWIDTH=1,CODECS="avc1.4d400d,mp4a.40.5"\n' + | ||
1186 | 'media.m3u8\n' + | ||
1187 | '#EXT-X-STREAM-INF:BANDWIDTH=10,CODECS="avc1.4d400f,mp4a.40.5"\n' + | ||
1188 | 'media1.m3u8\n'); // master | ||
1189 | |||
1190 | standardXHRResponse(requests.shift()); // media | ||
1191 | master = player.tech_.hls.playlists.master; | ||
1192 | strictEqual(master.playlists[0].excludeUntil, undefined, 'did not blacklist'); | ||
1193 | strictEqual(master.playlists[1].excludeUntil, undefined, 'did not blacklist'); | ||
1194 | }); | ||
1195 | |||
1196 | test('does not blacklist compatible AAC codec strings', function() { | ||
1197 | var master; | ||
1198 | player.src({ | ||
1199 | src: 'manifest/master.m3u8', | ||
1200 | type: 'application/vnd.apple.mpegurl' | ||
1201 | }); | ||
1202 | openMediaSource(player); | ||
1203 | |||
1204 | player.tech_.hls.bandwidth = 1; | ||
1205 | requests.shift().respond(200, null, | ||
1206 | '#EXTM3U\n' + | ||
1207 | '#EXT-X-STREAM-INF:BANDWIDTH=1,CODECS="avc1.4d400d,mp4a.40.2"\n' + | ||
1208 | 'media.m3u8\n' + | ||
1209 | '#EXT-X-STREAM-INF:BANDWIDTH=10,CODECS="avc1.4d400d,mp4a.40.3"\n' + | ||
1210 | 'media1.m3u8\n'); // master | ||
1211 | |||
1212 | standardXHRResponse(requests.shift()); // media | ||
1213 | master = player.tech_.hls.playlists.master; | ||
1214 | strictEqual(master.playlists[0].excludeUntil, undefined, 'did not blacklist'); | ||
1215 | strictEqual(master.playlists[1].excludeUntil, undefined, 'did not blacklist'); | ||
1216 | }); | ||
1217 | |||
1174 | test('blacklists switching between playlists with incompatible audio codecs', function() { | 1218 | test('blacklists switching between playlists with incompatible audio codecs', function() { |
1175 | var alternatePlaylist; | 1219 | var alternatePlaylist; |
1176 | player.src({ | 1220 | player.src({ | ... | ... |
-
Please register or sign in to post a comment