Skip to content

Commit

Permalink
fix(HLS): Allow detect mimeType from non-gap segments (#6892)
Browse files Browse the repository at this point in the history
  • Loading branch information
avelad committed Jun 25, 2024
1 parent 5e612c9 commit 1a3f6ae
Showing 1 changed file with 39 additions and 45 deletions.
84 changes: 39 additions & 45 deletions lib/hls/hls_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ shaka.hls.HlsParser = class {
}

const {segments, bandwidth} = this.createSegments_(
playlist, stream, mediaSequenceToStartTime, mediaVariables,
playlist, mediaSequenceToStartTime, mediaVariables,
streamInfo.getUris, streamInfo.type);
if (bandwidth) {
stream.bandwidth = bandwidth;
Expand Down Expand Up @@ -2302,6 +2302,13 @@ shaka.hls.HlsParser = class {
/** @type {!shaka.hls.Playlist} */
const playlist = this.manifestTextParser_.parsePlaylist(response.data);

if (playlist.type != shaka.hls.PlaylistType.MEDIA) {
throw new shaka.util.Error(
shaka.util.Error.Severity.CRITICAL,
shaka.util.Error.Category.MANIFEST,
shaka.util.Error.Code.HLS_INVALID_PLAYLIST_HIERARCHY);
}

/** @type {!Array.<!shaka.hls.Tag>} */
const variablesTags = shaka.hls.Utils.filterTagsByName(playlist.tags,
'EXT-X-DEFINE');
Expand Down Expand Up @@ -2631,15 +2638,6 @@ shaka.hls.HlsParser = class {
getUris, responseUri, codecs, type, languageValue, primary, name,
channelsCount, closedCaptions, characteristics, forced, sampleRate,
spatialAudio, mimeType = undefined) {
if (playlist.type != shaka.hls.PlaylistType.MEDIA) {
// EXT-X-MEDIA and EXT-X-IMAGE-STREAM-INF tags should point to media
// playlists.
throw new shaka.util.Error(
shaka.util.Error.Severity.CRITICAL,
shaka.util.Error.Category.MANIFEST,
shaka.util.Error.Code.HLS_INVALID_PLAYLIST_HIERARCHY);
}

goog.asserts.assert(playlist.segments != null,
'Media playlist should have segments!');

Expand All @@ -2649,9 +2647,14 @@ shaka.hls.HlsParser = class {
this.determineLastTargetDuration_(playlist);
}

const mediaSequenceToStartTime = this.isLive_() ?
this.mediaSequenceToStartTimeByType_.get(type) : new Map();

const {segments, bandwidth} = this.createSegments_(
playlist, mediaSequenceToStartTime, variables, getUris, type);

if (!mimeType) {
mimeType = await this.guessMimeType_(type, codecs, playlist,
variables, getUris);
mimeType = await this.guessMimeType_(type, codecs, segments);
}

const {drmInfos, keyIds, encrypted, aesEncrypted} =
Expand All @@ -2671,16 +2674,10 @@ shaka.hls.HlsParser = class {
stream.drmInfos = drmInfos;
stream.keyIds = keyIds;
stream.mimeType = mimeType;
this.setFullTypeForStream_(stream);

const mediaSequenceToStartTime = this.isLive_() ?
this.mediaSequenceToStartTimeByType_.get(type) : new Map();

const {segments, bandwidth} = this.createSegments_(
playlist, stream, mediaSequenceToStartTime, variables, getUris, type);
if (bandwidth) {
stream.bandwidth = bandwidth;
}
this.setFullTypeForStream_(stream);

// This new calculation is necessary for Low Latency streams.
if (this.isLive_()) {
Expand Down Expand Up @@ -3311,15 +3308,15 @@ shaka.hls.HlsParser = class {
* @param {number} startTime
* @param {!Map.<string, string>} variables
* @param {!shaka.hls.Playlist} playlist
* @param {shaka.extern.Stream} stream
* @param {string} type
* @param {function():!Array.<string>} getUris
* @param {shaka.extern.aesKey=} aesKey
* @return {shaka.media.SegmentReference}
* @private
*/
createSegmentReference_(
initSegmentReference, previousReference, hlsSegment, startTime,
variables, playlist, stream, getUris, aesKey) {
variables, playlist, type, getUris, aesKey) {
const tags = hlsSegment.tags;
const extinfTag =
shaka.hls.Utils.getFirstTagWithName(tags, 'EXTINF');
Expand Down Expand Up @@ -3376,8 +3373,8 @@ shaka.hls.HlsParser = class {
let isPreloadSegment = false;

if (this.lowLatencyMode_ && hlsSegment.partialSegments.length) {
const byterangeOptimizationSupport = (stream.mimeType == 'video/mp4' ||
stream.mimeType == 'audio/mp4') && window.ReadableStream &&
const byterangeOptimizationSupport =
initSegmentReference && window.ReadableStream &&
this.config_.hls.allowLowLatencyByteRangeOptimization;

let partialSyncTime = syncTime;
Expand Down Expand Up @@ -3536,7 +3533,7 @@ shaka.hls.HlsParser = class {

let tilesLayout = '';
let tileDuration = null;
if (stream.type == shaka.util.ManifestParserUtils.ContentType.IMAGE) {
if (type == shaka.util.ManifestParserUtils.ContentType.IMAGE) {
// By default in HLS the tilesLayout is 1x1
tilesLayout = '1x1';
const tilesTag =
Expand Down Expand Up @@ -3630,7 +3627,6 @@ shaka.hls.HlsParser = class {
* playlist.
*
* @param {!shaka.hls.Playlist} playlist
* @param {shaka.extern.Stream} stream
* @param {!Map.<number, number>} mediaSequenceToStartTime
* @param {!Map.<string, string>} variables
* @param {function():!Array.<string>} getUris
Expand All @@ -3639,7 +3635,7 @@ shaka.hls.HlsParser = class {
* bandwidth: (number|undefined)}}
* @private
*/
createSegments_(playlist, stream, mediaSequenceToStartTime, variables,
createSegments_(playlist, mediaSequenceToStartTime, variables,
getUris, type) {
/** @type {Array.<!shaka.hls.Segment>} */
const hlsSegments = playlist.segments;
Expand Down Expand Up @@ -3731,7 +3727,7 @@ shaka.hls.HlsParser = class {
startTime,
variables,
playlist,
stream,
type,
getUris,
aesKey);

Expand Down Expand Up @@ -3930,39 +3926,37 @@ shaka.hls.HlsParser = class {
}

/**
* Attempts to guess stream's mime type based on content type, URI, and
* contents of the playlist.
* Attempts to guess stream's mime type.
*
* @param {string} contentType
* @param {string} codecs
* @param {!shaka.hls.Playlist} playlist
* @param {!Map.<string, string>} variables
* @param {function():!Array.<string>} getUris
* @param {!Array.<!shaka.media.SegmentReference>} segments
* @return {!Promise.<string>}
* @private
*/
async guessMimeType_(contentType, codecs, playlist, variables, getUris) {
async guessMimeType_(contentType, codecs, segments) {
const HlsParser = shaka.hls.HlsParser;
const requestType = shaka.net.NetworkingEngine.RequestType.SEGMENT;

// If you wait long enough, requesting the first segment can fail
// because it has fallen off the left edge of DVR, so to be safer,
// let's request the middle segment.
goog.asserts.assert(playlist.segments.length,
'Playlist should have segments!');
const middleSegmentIdx = Math.trunc((playlist.segments.length - 1) / 2);
goog.asserts.assert(segments.length, 'Should have segments!');
let segmentIndex = Math.trunc((segments.length - 1) / 2);
let segment = segments[segmentIndex];
while (segment.status == shaka.media.SegmentReference.Status.MISSING &&
segmentIndex < segments.length) {
segmentIndex ++;
segment = segments[segmentIndex];
}

const middleSegment = playlist.segments[middleSegmentIdx];
if (shaka.hls.Utils.getFirstTagWithName(middleSegment.tags, 'EXT-X-GAP')) {
if (segment.status == shaka.media.SegmentReference.Status.MISSING) {
return this.guessMimeTypeFallback_(contentType);
}

const middleSegmentUris = shaka.hls.Utils.constructSegmentUris(
getUris(),
middleSegment.verbatimSegmentUri,
variables);
const segmentUris = segment.getUris();

const parsedUri = new goog.Uri(middleSegmentUris[0]);
const parsedUri = new goog.Uri(segmentUris[0]);
const extension = parsedUri.getPath().split('.').pop();
const map = HlsParser.EXTENSION_MAP_BY_CONTENT_TYPE_[contentType];

Expand All @@ -3987,7 +3981,7 @@ shaka.hls.HlsParser = class {
let contentMimeType;
const type = shaka.net.NetworkingEngine.AdvancedRequestType.MEDIA_SEGMENT;
const headRequest = shaka.net.NetworkingEngine.makeRequest(
middleSegmentUris, this.config_.retryParameters);
segmentUris, this.config_.retryParameters);
try {
headRequest.method = 'HEAD';
const response = await this.makeNetworkRequest_(
Expand All @@ -4008,7 +4002,7 @@ shaka.hls.HlsParser = class {

if (contentMimeType) {
// Split the MIME type in case the server sent additional parameters.
return contentMimeType.split(';')[0];
return contentMimeType.toLowerCase().split(';')[0];
}

return this.guessMimeTypeFallback_(contentType);
Expand Down

0 comments on commit 1a3f6ae

Please sign in to comment.