mirror of
https://github.com/shaka-project/shaka-player.git
synced 2026-06-17 16:26:39 +03:00
feat(hls): parse EXT-X-GAP (#4134)
Parse EXT-X-GAP HLS tag and add a status enum to shaka.media.SegmentReference. shaka.media.SegmentReference.Status.AVAILABLE --> Normal behaviour shaka.media.SegmentReference.Status. UNAVAILABLE --> Related to https://github.com/shaka-project/shaka-player/issues/2541 shaka.media.SegmentReference.Status. MISSING --> EXT-X-GAP in HLS Note: only the parsing is added, but the functionality is not yet implemented. Issue https://github.com/shaka-project/shaka-player/issues/1308
This commit is contained in:
committed by
GitHub
parent
4fecfb9652
commit
42eecc84f9
@@ -1865,6 +1865,11 @@ shaka.hls.HlsParser = class {
|
||||
}
|
||||
}
|
||||
|
||||
let status = shaka.media.SegmentReference.Status.AVAILABLE;
|
||||
if (shaka.hls.Utils.getFirstTagWithName(tags, 'EXT-X-GAP')) {
|
||||
status = shaka.media.SegmentReference.Status.MISSING;
|
||||
}
|
||||
|
||||
// Create SegmentReferences for the partial segments.
|
||||
const partialSegmentRefs = [];
|
||||
if (this.lowLatencyMode_ && hlsSegment.partialSegments.length) {
|
||||
@@ -1978,6 +1983,7 @@ shaka.hls.HlsParser = class {
|
||||
tilesLayout,
|
||||
tileDuration,
|
||||
syncTime,
|
||||
status,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -162,12 +162,15 @@ shaka.media.SegmentReference = class {
|
||||
* @param {?number=} syncTime
|
||||
* A time value, expressed in the same scale as the start and end time, which
|
||||
* is used to synchronize between streams.
|
||||
* @param {shaka.media.SegmentReference.Status=} status
|
||||
* The segment status is used to indicate that a segment does not exist or is
|
||||
* not available.
|
||||
*/
|
||||
constructor(
|
||||
startTime, endTime, uris, startByte, endByte, initSegmentReference,
|
||||
timestampOffset, appendWindowStart, appendWindowEnd,
|
||||
partialReferences = [], tilesLayout = '', tileDuration = null,
|
||||
syncTime = null) {
|
||||
syncTime = null, status = shaka.media.SegmentReference.Status.AVAILABLE) {
|
||||
// A preload hinted Partial Segment has the same startTime and endTime.
|
||||
goog.asserts.assert(startTime <= endTime,
|
||||
'startTime must be less than or equal to endTime');
|
||||
@@ -220,6 +223,9 @@ shaka.media.SegmentReference = class {
|
||||
|
||||
/** @type {?number} */
|
||||
this.syncTime = syncTime;
|
||||
|
||||
/** @type {shaka.media.SegmentReference.Status} */
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -315,6 +321,39 @@ shaka.media.SegmentReference = class {
|
||||
getTileDuration() {
|
||||
return this.tileDuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the segment's status.
|
||||
*
|
||||
* @return {shaka.media.SegmentReference.Status}
|
||||
* @export
|
||||
*/
|
||||
getStatus() {
|
||||
return this.status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the reference as unavailable.
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
markAsUnavailable() {
|
||||
this.status = shaka.media.SegmentReference.Status.UNAVAILABLE;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Rather than using booleans to communicate what the state of the reference,
|
||||
* we have this enum.
|
||||
*
|
||||
* @enum {number}
|
||||
* @export
|
||||
*/
|
||||
shaka.media.SegmentReference.Status = {
|
||||
AVAILABLE: 0,
|
||||
UNAVAILABLE: 1,
|
||||
MISSING: 2,
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -1424,6 +1424,105 @@ describe('HlsParser', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('parse EXT-X-GAP', async () => {
|
||||
const master = [
|
||||
'#EXTM3U\n',
|
||||
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",',
|
||||
'CHANNELS="2",URI="audio"\n',
|
||||
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
|
||||
'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1"\n',
|
||||
'video\n',
|
||||
].join('');
|
||||
|
||||
const video = [
|
||||
'#EXTM3U\n',
|
||||
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
||||
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
||||
'#EXTINF:5,\n',
|
||||
'main.mp4\n',
|
||||
'#EXTINF:5,\n',
|
||||
'#EXT-X-GAP\n',
|
||||
'main.mp4\n',
|
||||
'#EXTINF:5,\n',
|
||||
'main.mp4\n',
|
||||
].join('');
|
||||
|
||||
const audio = [
|
||||
'#EXTM3U\n',
|
||||
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
||||
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
||||
'#EXTINF:5,\n',
|
||||
'main.mp4\n',
|
||||
'#EXTINF:5,\n',
|
||||
'#EXT-X-GAP\n',
|
||||
'main.mp4\n',
|
||||
'#EXTINF:5,\n',
|
||||
'main.mp4\n',
|
||||
].join('');
|
||||
|
||||
fakeNetEngine
|
||||
.setResponseText('test:/master', master)
|
||||
.setResponseText('test:/audio', audio)
|
||||
.setResponseText('test:/video', video)
|
||||
.setResponseValue('test:/init.mp4', initSegmentData)
|
||||
.setResponseValue('test:/main.mp4', segmentData);
|
||||
|
||||
const actual = await parser.start('test:/master', playerInterface);
|
||||
|
||||
expect(actual.variants.length).toBe(1);
|
||||
|
||||
const variant = actual.variants[0];
|
||||
expect(variant.video).toBeTruthy();
|
||||
expect(variant.audio).toBeTruthy();
|
||||
|
||||
const available = shaka.media.SegmentReference.Status.AVAILABLE;
|
||||
const missing = shaka.media.SegmentReference.Status.MISSING;
|
||||
|
||||
await variant.video.createSegmentIndex();
|
||||
goog.asserts.assert(variant.video.segmentIndex != null,
|
||||
'Null segmentIndex!');
|
||||
|
||||
const firstVideoReference = variant.video.segmentIndex.get(0);
|
||||
const secondVideoReference = variant.video.segmentIndex.get(1);
|
||||
const thirdVideoReference = variant.video.segmentIndex.get(2);
|
||||
|
||||
expect(firstVideoReference).not.toBe(null);
|
||||
expect(secondVideoReference).not.toBe(null);
|
||||
expect(thirdVideoReference).not.toBe(null);
|
||||
|
||||
if (firstVideoReference) {
|
||||
expect(firstVideoReference.getStatus()).toBe(available);
|
||||
}
|
||||
if (secondVideoReference) {
|
||||
expect(secondVideoReference.getStatus()).toBe(missing);
|
||||
}
|
||||
if (thirdVideoReference) {
|
||||
expect(thirdVideoReference.getStatus()).toBe(available);
|
||||
}
|
||||
|
||||
await variant.audio.createSegmentIndex();
|
||||
goog.asserts.assert(variant.audio.segmentIndex != null,
|
||||
'Null segmentIndex!');
|
||||
|
||||
const firstAudioReference = variant.audio.segmentIndex.get(0);
|
||||
const secondAudioReference = variant.audio.segmentIndex.get(1);
|
||||
const thirdAudioReference = variant.audio.segmentIndex.get(2);
|
||||
|
||||
expect(firstAudioReference).not.toBe(null);
|
||||
expect(secondAudioReference).not.toBe(null);
|
||||
expect(thirdAudioReference).not.toBe(null);
|
||||
|
||||
if (firstAudioReference) {
|
||||
expect(firstAudioReference.getStatus()).toBe(available);
|
||||
}
|
||||
if (secondAudioReference) {
|
||||
expect(secondAudioReference.getStatus()).toBe(missing);
|
||||
}
|
||||
if (thirdAudioReference) {
|
||||
expect(thirdAudioReference.getStatus()).toBe(available);
|
||||
}
|
||||
});
|
||||
|
||||
it('Disable audio does not create audio streams', async () => {
|
||||
const master = [
|
||||
'#EXTM3U\n',
|
||||
|
||||
Reference in New Issue
Block a user