Skip to content

Commit

Permalink
Fixes parsing Microsoft-packaged HLS.
Browse files Browse the repository at this point in the history
Microsoft HLS packaging tools generate audio-only variants in an odd way;
specifically, the variants have an AUDIO tag despite being audio-only, and
thus double-link to the stream.
Previously, the way we detected stream type lead to us assigning one version
of the audio stream to audio and one to video, thus erroneously making those
variants appear to be video+audio variants.
This makes it so that, if the AUDIO tag has the same content URI as the base
stream, it only uses the version in the AUDIO tag.

Change-Id: Ie940970587e95a9020ed67589042008d0568e153
  • Loading branch information
theodab authored and joeyparrish committed Jul 17, 2017
1 parent f327a66 commit d05a6b7
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 10 deletions.
44 changes: 34 additions & 10 deletions lib/hls/hls_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ shaka.hls.HlsParser = function() {
* @typedef {{
* stream: !shakaExtern.Stream,
* segmentIndex: !shaka.media.SegmentIndex,
* drmInfos: !Array.<shakaExtern.DrmInfo>
* drmInfos: !Array.<shakaExtern.DrmInfo>,
* relativeUri: !string
* }}
*
* @description
Expand All @@ -90,6 +91,8 @@ shaka.hls.HlsParser = function() {
* SegmentIndex of the stream.
* @property {!Array.<shakaExtern.DrmInfo>} drmInfos
* DrmInfos of the stream. There may be multiple for multi-DRM content.
* @property {!string} relativeUri
* The uri associated with the stream, relative to the manifest.
*/
shaka.hls.HlsParser.StreamInfo;

Expand Down Expand Up @@ -299,6 +302,7 @@ shaka.hls.HlsParser.prototype.createVariantsForTag_ = function(tag, playlist) {
if (!audioStreamInfos.length && !videoStreamInfos.length) {
// There are no associated streams. This is either an audio-only stream,
// a video-only stream, or a multiplexed stream.
var ignoreStream = false;

if (codecs.length == 1) {
// There is only one codec, so it shouldn't be multiplexed.
Expand All @@ -322,9 +326,22 @@ shaka.hls.HlsParser.prototype.createVariantsForTag_ = function(tag, playlist) {
codecs = [codecs.join(',')];
}
} else if (audioStreamInfos.length) {
// There are associated audio streams. Assume this is video.
shaka.log.debug('Guessing video.');
type = ContentType.VIDEO;
var streamURI = HlsParser.getRequiredAttributeValue_(tag, 'URI');
var firstAudioStreamURI = audioStreamInfos[0].relativeUri;
if (streamURI == firstAudioStreamURI) {
// The Microsoft HLS manifest generators will make audio-only variants
// that link to their URI both directly and through an audio tag.
// In that case, ignore the local URI and use the version in the
// AUDIO tag, so you inherit its language.
// As an example, see the manifest linked in issue #860.
shaka.log.debug('Guessing audio-only.');
type = ContentType.AUDIO;
ignoreStream = true;
} else {
// There are associated audio streams. Assume this is video.
shaka.log.debug('Guessing video.');
type = ContentType.VIDEO;
}
} else {
// There are associated video streams. Assume this is audio.
goog.asserts.assert(videoStreamInfos.length,
Expand All @@ -334,14 +351,19 @@ shaka.hls.HlsParser.prototype.createVariantsForTag_ = function(tag, playlist) {
}

goog.asserts.assert(type, 'Type should have been set by now!');
if (ignoreStream)
return Promise.resolve();
return this.createStreamInfoFromVariantTag_(tag, codecs, type, timeOffset);
}.bind(this)).then(function(streamInfo) {
goog.asserts.assert(streamInfo, 'We should have created a stream!');
if (streamInfo.stream.type == ContentType.AUDIO) {
audioStreamInfos = [streamInfo];
} else {
videoStreamInfos = [streamInfo];
if (streamInfo) {
if (streamInfo.stream.type == ContentType.AUDIO) {
audioStreamInfos = [streamInfo];
} else {
videoStreamInfos = [streamInfo];
}
}
goog.asserts.assert(videoStreamInfos || audioStreamInfos,
'We should have created a stream!');

return this.createVariants_(
audioStreamInfos,
Expand Down Expand Up @@ -558,6 +580,7 @@ shaka.hls.HlsParser.prototype.createStreamInfo_ =
var Utils = shaka.hls.Utils;
var ContentType = shaka.util.ManifestParserUtils.ContentType;
var HlsParser = shaka.hls.HlsParser;
var relativeUri = uri;
uri = Utils.constructAbsoluteUri(this.manifestUri_, uri);

return this.requestManifest_(uri).then(function(response) {
Expand Down Expand Up @@ -684,7 +707,8 @@ shaka.hls.HlsParser.prototype.createStreamInfo_ =
return {
stream: stream,
segmentIndex: segmentIndex,
drmInfos: drmInfos
drmInfos: drmInfos,
relativeUri: relativeUri
};
}.bind(this));
}.bind(this));
Expand Down
34 changes: 34 additions & 0 deletions test/hls/hls_parser_unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,40 @@ describe('HlsParser', function() {
testHlsParser(master, media, manifest, done);
});

it('handles audio tags on audio streams', function(done) {
var master = [
'#EXTM3U\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="mp4a",AUDIO="aud1"\n',
'test://audio\n',
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",',
'URI="test://audio"\n'
].join('');

var media = [
'#EXTM3U\n',
'#EXT-X-MAP:URI="test://main.mp4",BYTERANGE="616@0"\n',
'#EXTINF:5,\n',
'#EXT-X-BYTERANGE:121090@616\n',
'test://main.mp4'
].join('');

var manifest = new shaka.test.ManifestGenerator()
.anyTimeline()
.addPeriod(jasmine.any(Number))
.addVariant(jasmine.any(Number))
.language('en')
.bandwidth(200)
.addAudio(jasmine.any(Number))
.language('en')
.anySegmentFunctions()
.anyInitSegment()
.presentationTimeOffset(0)
.mime('audio/mp4', 'mp4a')
.build();

testHlsParser(master, media, manifest, done);
});

it('parses multiplexed variant', function(done) {
var master = [
'#EXTM3U\n',
Expand Down

0 comments on commit d05a6b7

Please sign in to comment.