Skip to content
This repository was archived by the owner on Oct 20, 2022. It is now read-only.

Commit ee84f4e

Browse files
committed
Merge branch 'release/1.9.0'
2 parents 09c548e + eb8785c commit ee84f4e

34 files changed

+707
-541
lines changed

RELEASES NOTES.txt

+21
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,24 @@
1+
### Release Notes v1.9.0 (2017/03/08)
2+
* Consolidate EME/Protection layer management:
3+
* - Start playback only once ProtectionController is initialized (avoids conflicts on Chrome when setting MediaKeys on <video>)
4+
* - Reset Streams and the <video> source before resetting ProtectionController and MediaKeys (avoid playback issue on Firefox)
5+
* - Correct application-level's license persistence management
6+
* - Listen for 'waitingforkey' event only once the license is usable
7+
* - Add new error code MEDIA_KEYERR_EXPIRED for expired licences (instead of MEDIA_ERR_ENCRYPTED)
8+
* Enrich MEDIA_ERR_CODEC_UNSUPPORTED error's data
9+
* Stop/reset current playback when an error is raised
10+
* [MSS] Consider empty FourCC field only for audio streams
11+
* [HLS] Add error code HLS_INVALID_PACKET_ERROR in case of invalid MPEG2-TS chunks (for example if unsuccessfully decrypted)
12+
* [HLS] Add error code HLS_INVALID_KEY_ERROR in case of invalid AES keys
13+
* Bugs fixing:
14+
* - [HLS] Fix H.264 IDR access units detection
15+
* - [HLS] Fix segment lists processing when switching among alternative tracks
16+
* - [HLS] Fix reset process when AES key file is being downloaded
17+
* - [HLS] Fix DVR window refresh when playback is paused
18+
* - [MSS] Fix MP4 media fragments processing in case of trick play (when sample_duration is set as default in tfhd)
19+
* - Fix VTT parser if X-TIMESTAMP-MAP filed is missing
20+
* - Fix ABR DownloadRatioRule's bandwidth calculation in case of aborted requests
21+
122
### Release Notes v1.8.0 (2017/01/23)
223
* Implements license persistence at application layer (protection model 21Jan2015) according to player configuration (by default no license persistence)
324
* [MSS] Add support for MSS static streams starting at t > 0 (live delinearization use case)

app/js/dash/FragmentExtensions.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,8 @@ Dash.dependencies.FragmentExtensions = function () {
141141
deferred.reject(errorStr);
142142
};
143143

144-
request.responseType = "arraybuffer";
145144
request.open("GET", url);
145+
request.responseType = "arraybuffer";
146146
request.send(null);
147147

148148
return deferred.promise;
@@ -158,4 +158,4 @@ Dash.dependencies.FragmentExtensions = function () {
158158

159159
Dash.dependencies.FragmentExtensions.prototype = {
160160
constructor: Dash.dependencies.FragmentExtensions
161-
};
161+
};

app/js/hls/HlsDemux.js

+14-1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ Hls.dependencies.HlsDemux = function() {
5252
return null;
5353
},
5454

55+
checkTsPacket = function(data) {
56+
var tsPacket = new mpegts.ts.TsPacket();
57+
return tsPacket.checkSyncWord(data.subarray(0, mpegts.ts.TsPacket.prototype.TS_PACKET_SIZE));
58+
},
59+
5560
getPAT = function(data) {
5661
var tsPacket = getTsPacket.call(this, data, 0, mpegts.ts.TsPacket.prototype.PAT_PID);
5762

@@ -434,6 +439,14 @@ Hls.dependencies.HlsDemux = function() {
434439
track,
435440
streamTypeDesc;
436441

442+
// First, check that packet is really a TS packet
443+
if( !checkTsPacket.call(this,data) ) {
444+
throw {
445+
name: MediaPlayer.dependencies.ErrorHandler.prototype.HLS_INVALID_PACKET_ERROR,
446+
message: "Failed to demux, packet is invalid, missing SYNC byte"
447+
};
448+
}
449+
437450
// Get PSI (PAT, PMT)
438451
pat = getPAT.call(this, data);
439452
if (pat === null) {
@@ -596,4 +609,4 @@ Hls.dependencies.HlsDemux = function() {
596609

597610
Hls.dependencies.HlsDemux.prototype = {
598611
constructor: Hls.dependencies.HlsDemux
599-
};
612+
};

app/js/hls/HlsFragmentController.js

+28-15
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@
1313
*
1414
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1515
*/
16-
Hls.dependencies.HlsFragmentController = function() {
16+
Hls.dependencies.HlsFragmentController = function () {
1717
"use strict";
1818

1919
var decryptionInfos = {},
2020

21-
generateMediaSegment = function(data, request) {
21+
generateMediaSegment = function (data, request) {
2222
var i = 0,
2323
// Demultiplex HLS chunk to get samples
2424
tracks = rslt.hlsDemux.demux(new Uint8Array(data), request);
@@ -42,7 +42,7 @@ Hls.dependencies.HlsFragmentController = function() {
4242
return rslt.mp4Processor.generateInitMediaSegment(tracks);
4343
},
4444

45-
createInitializationVector = function(segmentNumber) {
45+
createInitializationVector = function (segmentNumber) {
4646
var uint8View = new Uint8Array(16),
4747
i = 0;
4848

@@ -53,7 +53,7 @@ Hls.dependencies.HlsFragmentController = function() {
5353
return uint8View;
5454
},
5555

56-
decrypt = function(data, decryptionInfo) {
56+
decrypt = function (data, decryptionInfo) {
5757

5858
var t = new Date();
5959

@@ -79,7 +79,7 @@ Hls.dependencies.HlsFragmentController = function() {
7979
return decrypter.decrypt(data);
8080
},
8181

82-
loadDecryptionKey = function(decryptionInfo) {
82+
loadDecryptionKey = function (decryptionInfo) {
8383
var deferred = Q.defer();
8484

8585
this.debug.log("[HlsFragmentController]", "Load decryption key: " + decryptionInfo.uri);
@@ -91,7 +91,7 @@ Hls.dependencies.HlsFragmentController = function() {
9191
decryptionInfo.key = new Uint8Array(request.response);
9292
deferred.resolve();
9393
},
94-
function(request) {
94+
function (request) {
9595
if (!request || request.aborted) {
9696
deferred.reject();
9797
} else {
@@ -110,7 +110,7 @@ Hls.dependencies.HlsFragmentController = function() {
110110
return deferred.promise;
111111
},
112112

113-
decryptSegment = function(bytes, request) {
113+
decryptSegment = function (bytes, request) {
114114
var deferred = Q.defer(),
115115
decryptionInfo,
116116
self = this;
@@ -130,7 +130,15 @@ Hls.dependencies.HlsFragmentController = function() {
130130
} else {
131131
decryptionInfo = request.decryptionInfo;
132132
loadDecryptionKey.call(this, decryptionInfo).then(
133-
function() {
133+
function () {
134+
135+
// check key
136+
if (decryptionInfo.key && decryptionInfo.key.byteLength !== 16) {
137+
return deferred.reject({
138+
name: MediaPlayer.dependencies.ErrorHandler.prototype.HLS_INVALID_KEY_ERROR,
139+
message: "Invalid HLS key - Key length (" + decryptionInfo.key.byteLength + ") does not respect specification"
140+
});
141+
}
134142
decryptionInfos[decryptionInfo.uri] = decryptionInfo;
135143
deferred.resolve(decrypt.call(self, bytes, decryptionInfo));
136144
},
@@ -149,7 +157,7 @@ Hls.dependencies.HlsFragmentController = function() {
149157
rslt.hlsDemux = undefined;
150158
rslt.mp4Processor = undefined;
151159

152-
rslt.process = function(bytes, request/*, representation*/) {
160+
rslt.process = function (bytes, request /*, representation*/ ) {
153161
var deferred = Q.defer(),
154162
result = null;
155163

@@ -170,13 +178,18 @@ Hls.dependencies.HlsFragmentController = function() {
170178
}
171179

172180
// Decrypt the segment if encrypted
173-
decryptSegment.call(rslt, bytes, request).then(function(data) {
181+
decryptSegment.call(rslt, bytes, request).then(function (data) {
174182
//console.saveBinArray(data, request.url.substring(request.url.lastIndexOf('/') + 1));
175183
try {
176-
// Generate media segment (moof) from demultiplexed MPEG2-TS chunk
177-
result = generateMediaSegment(data, request);
178-
rslt.sequenceNumber++;
179-
deferred.resolve(result);
184+
// First check stream has not been reset while decrypting the chunk
185+
if (!rslt.manifestModel.getValue()) {
186+
deferred.resolve(null);
187+
} else {
188+
// Generate media segment (moof) from demultiplexed MPEG2-TS chunk
189+
result = generateMediaSegment(data, request);
190+
rslt.sequenceNumber++;
191+
deferred.resolve(result);
192+
}
180193
} catch (e) {
181194
deferred.reject(e);
182195
}
@@ -193,4 +206,4 @@ Hls.dependencies.HlsFragmentController = function() {
193206

194207
Hls.dependencies.HlsFragmentController.prototype = {
195208
constructor: Hls.dependencies.HlsFragmentController
196-
};
209+
};

app/js/hls/HlsParser.js

+36-17
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ Hls.dependencies.HlsParser = function() {
153153
return false;
154154
}
155155

156-
this.debug.log(manifest);
156+
// this.debug.log(manifest);
157157

158158
if (manifest.indexOf('#EXTM3U') !== 0) {
159159
return false;
@@ -290,13 +290,15 @@ Hls.dependencies.HlsParser = function() {
290290
postProcess = function(manifest, quality) {
291291
var deferred = Q.defer(),
292292
period = manifest.Period_asArray[0],
293+
// Consider video AdaptationSet (always the 1st one)
293294
adaptationSet = period.AdaptationSet_asArray[0],
295+
// Consider representation of current and downloaded quality
294296
representation = adaptationSet.Representation_asArray[quality],
295297
request = new MediaPlayer.vo.SegmentRequest(),
296-
self = this,
297298
manifestDuration,
298-
mpdLoadedTime;
299-
299+
mpdLoadedTime,
300+
maxSequenceNumber,
301+
i, j, k;
300302

301303
period.start = 0; //segmentTimes[adaptationSet.Representation_asArray[0].SegmentList.startNumber];
302304

@@ -315,20 +317,38 @@ Hls.dependencies.HlsParser = function() {
315317

316318
// Dynamic use case
317319
if (manifest.type === "dynamic") {
318-
// => set manifest refresh period as the duration of 1 fragment/chunk
319-
//manifest.minimumUpdatePeriod = representation.SegmentList.duration;
320-
321-
// => set availabilityStartTime property
320+
// Set availabilityStartTime property
322321
mpdLoadedTime = new Date();
323322
manifest.availabilityStartTime = new Date(mpdLoadedTime.getTime() - (manifestDuration * 1000));
324323

325-
// => set timeshift buffer depth
324+
// Set timeshift buffer depth
326325
manifest.timeShiftBufferDepth = manifestDuration;
327326
}
328327

329328
// Set minBufferTime
330329
manifest.minBufferTime = representation.SegmentList.duration * 3; //MediaPlayer.dependencies.BufferExtensions.DEFAULT_MIN_BUFFER_TIME
331330

331+
// Align segment lists of all adaptations
332+
maxSequenceNumber = Math.max.apply(null, period.AdaptationSet_asArray.map(function(adaptation) {
333+
var repIndex = quality > adaptation.Representation_asArray.length ? 0 : quality;
334+
return adaptation.Representation_asArray[repIndex].SegmentList.startNumber;
335+
}));
336+
for (i = 0; i < period.AdaptationSet_asArray.length; i++) {
337+
var adaptation = period.AdaptationSet_asArray[i];
338+
for (j = 0; j < adaptation.Representation_asArray.length; j++) {
339+
if (adaptation.Representation_asArray[j].SegmentList) {
340+
var segments = adaptation.Representation_asArray[j].SegmentList.SegmentURL_asArray;
341+
if (segments[0].sequenceNumber < maxSequenceNumber) {
342+
removeSegments(segments, maxSequenceNumber);
343+
segments[0].time = 0;
344+
for (k = 1; k < segments.length; k++) {
345+
segments[k].time = segments[k - 1].time + segments[k - 1].duration;
346+
}
347+
}
348+
}
349+
}
350+
}
351+
332352
// Download initialization data (PSI, IDR...) of 1st representation to obtain codec information
333353
representation = adaptationSet.Representation_asArray[quality];
334354
request.type = "Initialization Segment";
@@ -352,13 +372,12 @@ Hls.dependencies.HlsParser = function() {
352372
};
353373

354374
var onError = function() {
355-
// ERROR
356375
deferred.resolve();
357376
};
358377

359378
if (representation.codecs === "") {
360-
self.debug.log("[HlsParser]", "Load initialization segment: " + request.url);
361-
self.fragmentLoader.load(request).then(onLoaded.bind(self, representation), onError.bind(self));
379+
this.debug.log("[HlsParser]", "Load initialization segment: " + request.url);
380+
this.fragmentLoader.load(request).then(onLoaded.bind(this, representation), onError.bind(this));
362381
} else {
363382
deferred.resolve();
364383
}
@@ -380,8 +399,8 @@ Hls.dependencies.HlsParser = function() {
380399
},
381400

382401
updatePlaylist = function(representation, adaptation) {
383-
var deferred = Q.defer(),
384-
self = this;
402+
var self = this,
403+
deferred = Q.defer();
385404

386405
this.debug.log("[HlsParser]", "Load playlist manifest: " + representation.url);
387406
xhrLoader = new MediaPlayer.dependencies.XHRLoader();
@@ -420,7 +439,8 @@ Hls.dependencies.HlsParser = function() {
420439
},
421440

422441
processManifest = function(manifest, baseUrl) {
423-
var deferred = Q.defer(),
442+
var self = this,
443+
deferred = Q.defer(),
424444
mpd,
425445
period,
426446
adaptationsSets = [],
@@ -434,7 +454,6 @@ Hls.dependencies.HlsParser = function() {
434454
media,
435455
quality,
436456
playlistDefers = [],
437-
self = this,
438457
i = 0;
439458

440459
if (manifest.indexOf('#EXTM3U') !== 0) {
@@ -534,7 +553,7 @@ Hls.dependencies.HlsParser = function() {
534553
representation = adaptationsSets[0].Representation_asArray[quality];
535554
playlistDefers.push(updatePlaylist.call(this, representation, adaptationSet));
536555

537-
// alternative renditions of the same content (alternative audio tracks or subtitles) #EXT-X-MEDIA
556+
// Alternative renditions of the same content (alternative audio tracks or subtitles) #EXT-X-MEDIA
538557
medias = getMedias(manifest);
539558
for (i =0; i < medias.length; i++) {
540559
media = medias[i];

0 commit comments

Comments
 (0)