shaka-player
Advanced tools
Comparing version 2.1.3 to 2.1.4
@@ -1,2 +0,2 @@ | ||
**Have you read the [FAQ](https://goo.gl/njDlGk) and checked for duplicate issues**: | ||
**Have you read the [FAQ](https://goo.gl/JE1Sy5) and checked for duplicate issues**: | ||
@@ -3,0 +3,0 @@ **What version of Shaka Player are you using**: |
@@ -0,1 +1,38 @@ | ||
## 2.1.4 (2017-06-16) | ||
New features: | ||
- Allow role to be specified in selectAudioLanguage and selectTextLanguage | ||
- https://github.com/google/shaka-player/issues/767 | ||
Bugfixes: | ||
- Fix changing languages close to a period boundary | ||
- https://github.com/google/shaka-player/issues/797 | ||
- Fix hang in load() when there are pending failures | ||
- https://github.com/google/shaka-player/issues/782 | ||
- Fix DASH parser ignoring certain text streams | ||
- https://github.com/google/shaka-player/issues/875 | ||
- Fix exceptions when side-loading text tracks | ||
- https://github.com/google/shaka-player/issues/821 | ||
- Fix PlayReady support on Chromecast | ||
- https://github.com/google/shaka-player/issues/852 | ||
- Fix version number issues during publication on NPM | ||
- https://github.com/google/shaka-player/issues/869 | ||
- Fix pollution from npm on Windows | ||
- https://github.com/google/shaka-player/issues/776 | ||
- Fix support for npm v5 | ||
- https://github.com/google/shaka-player/issues/854 | ||
Demo app: | ||
- Fix control visibility in fullscreen mode on mobile phones | ||
- https://github.com/google/shaka-player/issues/663 | ||
Docs: | ||
- Updated welcome docs | ||
- Updated list of supported platforms | ||
- https://github.com/google/shaka-player/issues/863 | ||
- Updated FAQ | ||
- https://github.com/google/shaka-player/issues/864 | ||
- https://github.com/google/shaka-player/issues/865 | ||
## 2.1.3 (2017-06-06) | ||
@@ -2,0 +39,0 @@ |
@@ -255,2 +255,7 @@ /** | ||
// When there is a touch, we can get a 'mousemove' event after touch events. | ||
// This should be treated as part of the touch, which has already been handled | ||
if (this.lastTouchEventTime_ && event.type == 'mousemove') | ||
return; | ||
// Use the cursor specified in the CSS file. | ||
@@ -279,2 +284,6 @@ this.videoContainer_.style.cursor = ''; | ||
ShakaControls.prototype.onMouseOut_ = function() { | ||
// We sometimes get 'mouseout' events with touches. Since we can never leave | ||
// the video element when touching, ignore. | ||
if (this.lastTouchEventTime_) return; | ||
// Expire the timer early. | ||
@@ -281,0 +290,0 @@ if (this.mouseStillTimeoutId_) { |
@@ -1012,4 +1012,5 @@ /** | ||
* @param {!string} language | ||
* @param {string=} opt_role | ||
*/ | ||
shaka.Player.prototype.selectAudioLanguage = function(language) {}; | ||
shaka.Player.prototype.selectAudioLanguage = function(language, opt_role) {}; | ||
/** | ||
@@ -1019,4 +1020,5 @@ * Sets currentTextLanguage to the selected language and chooses | ||
* @param {!string} language | ||
* @param {string=} opt_role | ||
*/ | ||
shaka.Player.prototype.selectTextLanguage = function(language) {}; | ||
shaka.Player.prototype.selectTextLanguage = function(language, opt_role) {}; | ||
/** | ||
@@ -1023,0 +1025,0 @@ * @return {boolean} True if the current text track is visible. |
@@ -1012,4 +1012,5 @@ /** | ||
* @param {!string} language | ||
* @param {string=} opt_role | ||
*/ | ||
shaka.Player.prototype.selectAudioLanguage = function(language) {}; | ||
shaka.Player.prototype.selectAudioLanguage = function(language, opt_role) {}; | ||
/** | ||
@@ -1019,4 +1020,5 @@ * Sets currentTextLanguage to the selected language and chooses | ||
* @param {!string} language | ||
* @param {string=} opt_role | ||
*/ | ||
shaka.Player.prototype.selectTextLanguage = function(language) {}; | ||
shaka.Player.prototype.selectTextLanguage = function(language, opt_role) {}; | ||
/** | ||
@@ -1023,0 +1025,0 @@ * @return {boolean} True if the current text track is visible. |
@@ -14,2 +14,3 @@ [ | ||
{ "service-worker": { "title": "Service Worker Caching" } }, | ||
{ "faq": { "title": "Frequently Asked Questions" } }, | ||
{ "upgrade": { | ||
@@ -16,0 +17,0 @@ "title": "Upgrade Guide", |
@@ -28,6 +28,9 @@ # Welcome to Shaka Player | ||
```sh | ||
sudo apt-get install git python2.7 openjdk-7-jre-headless npm | ||
sudo npm install -g npm # Upgrade npm to the latest | ||
# Add a symlink missing on some systems: | ||
sudo ln -s /usr/bin/nodejs /usr/local/bin/node | ||
sudo apt-get update | ||
sudo apt-get install git python2.7 default-jre-headless npm | ||
# Upgrade npm and node to the latest versions | ||
sudo npm install -g n | ||
sudo n stable | ||
sudo npm install -g npm | ||
``` | ||
@@ -34,0 +37,0 @@ |
@@ -894,2 +894,4 @@ /** | ||
var ManifestParserUtils = shaka.util.ManifestParserUtils; | ||
var ContentType = ManifestParserUtils.ContentType; | ||
context.adaptationSet = this.createFrame_(elem, context.period, null); | ||
@@ -975,4 +977,9 @@ | ||
if (!context.adaptationSet.contentType) { | ||
// Guess the AdaptationSet's content type. | ||
// If AdaptationSet's type is unknown or is ambiguously "application", | ||
// guess based on the information in the first stream. If the attributes | ||
// mimeType and codecs are split across levels, they will both be inherited | ||
// down to the stream level by this point, so the stream will have all the | ||
// necessary information. | ||
if (!context.adaptationSet.contentType || | ||
context.adaptationSet.contentType == ContentType.APPLICATION) { | ||
var mimeType = streams[0].mimeType; | ||
@@ -979,0 +986,0 @@ var codecs = streams[0].codecs; |
@@ -1071,3 +1071,4 @@ /** | ||
if (this.currentDrmInfo_.keySystem == 'com.microsoft.playready') { | ||
if (this.currentDrmInfo_.keySystem == 'com.microsoft.playready' || | ||
this.currentDrmInfo_.keySystem == 'com.chromecast.playready') { | ||
this.unpackPlayReadyRequest_(request); | ||
@@ -1074,0 +1075,0 @@ } |
@@ -96,2 +96,7 @@ /** | ||
this.complete_ = true; | ||
if (this.canceled_) { | ||
this.onCancelComplete_(); | ||
return Promise.reject(this.rejectionValue_); | ||
} | ||
return Promise.reject(error); | ||
@@ -98,0 +103,0 @@ }.bind(this)); |
@@ -369,6 +369,7 @@ /** | ||
* @param {!Object=} opt_languageMatches | ||
* @param {string=} opt_role | ||
* @return {!Array.<!shakaExtern.Variant>} | ||
*/ | ||
shaka.util.StreamUtils.filterVariantsByRoleAndLanguage = function( | ||
period, preferredLanguage, opt_languageMatches) { | ||
period, preferredLanguage, opt_languageMatches, opt_role) { | ||
var LanguageUtils = shaka.util.LanguageUtils; | ||
@@ -390,3 +391,3 @@ var ContentType = shaka.util.ManifestParserUtils.ContentType; | ||
// Finally, choose based on language preference. Favor exact matches, then | ||
// Choose based on language preference. Favor exact matches, then | ||
// base matches, finally different subtags. Execute in reverse order so | ||
@@ -411,4 +412,5 @@ // the later steps override the previous ones. | ||
} | ||
if (opt_languageMatches) | ||
if (opt_languageMatches) { | ||
opt_languageMatches[ContentType.AUDIO] = true; | ||
} | ||
} | ||
@@ -419,2 +421,17 @@ }); // forEach(variant) | ||
// Choose based on role preference. If there's no exact match, return | ||
// what was chosen based on the language preference. | ||
var role = opt_role || ''; | ||
if (role) { | ||
var chosenWithRoles = chosen.filter(function(variant) { | ||
return (variant.audio && (variant.audio.roles.indexOf(role) > - 1)) || | ||
(variant.video && (variant.video.roles.indexOf(role) > - 1)); | ||
}); | ||
if (chosenWithRoles.length) return chosenWithRoles; | ||
else { | ||
shaka.log.warning( | ||
'No exact match for the role is found. Returning the selection ' + | ||
'based on language preference.'); | ||
} | ||
} | ||
return chosen; | ||
@@ -430,6 +447,7 @@ }; | ||
* @param {!Object=} opt_languageMatches | ||
* @param {string=} opt_role | ||
* @return {!Array.<!shakaExtern.Stream>} | ||
*/ | ||
shaka.util.StreamUtils.filterTextStreamsByRoleAndLanguage = function( | ||
period, preferredLanguage, opt_languageMatches) { | ||
period, preferredLanguage, opt_languageMatches, opt_role) { | ||
var LanguageUtils = shaka.util.LanguageUtils; | ||
@@ -474,3 +492,16 @@ var ContentType = shaka.util.ManifestParserUtils.ContentType; | ||
} // if (preferredLanguage) | ||
// Choose based on role preference. If there's no exact match, return | ||
// what was chosen based on the language preference. | ||
var role = opt_role || ''; | ||
if (role) { | ||
var chosenWithRoles = chosen.filter(function(stream) { | ||
return (stream && (stream.roles.indexOf(role) > - 1)); | ||
}); | ||
if (chosenWithRoles.length) return chosenWithRoles; | ||
else { | ||
shaka.log.warning( | ||
'No exact match for the role is found. Returning the selection ' + | ||
'based on language preference.'); | ||
} | ||
} | ||
return chosen; | ||
@@ -477,0 +508,0 @@ }; |
{ | ||
"name": "shaka-player", | ||
"description": "DASH/EME video player library", | ||
"version": "2.1.3", | ||
"version": "2.1.4", | ||
"homepage": "https://github.com/google/shaka-player", | ||
@@ -48,4 +48,4 @@ "author": "Google", | ||
"scripts": { | ||
"prepublish": "in-publish && python ./build/checkversion.py && python ./build/all.py && python ./build/build.py --debug || not-in-publish" | ||
"prepublish": "in-publish && python ./build/checkversion.py && python ./build/all.py --force && python ./build/build.py --force --debug || not-in-publish" | ||
} | ||
} |
@@ -15,5 +15,5 @@ # ![Shaka Player](docs/shaka-player-logo.png) | ||
It is actively tested with: | ||
- Chrome | ||
- Chrome on Linux, Mac, Windows, Android, and ChromeOS | ||
- Chromecast | ||
- Firefox | ||
- Firefox on Linux, Mac, and Windows | ||
- Microsoft Edge | ||
@@ -154,2 +154,3 @@ - IE 11 | ||
For general help and before filing any bugs, please read the [FAQ](docs/faq.md). | ||
For general help and before filing any bugs, please read the | ||
[FAQ](docs/tutorials/faq.md). |
@@ -849,2 +849,38 @@ /** | ||
it('handles text with mime and codecs on different levels', function(done) { | ||
// Regression test for #875 | ||
var manifestText = [ | ||
'<MPD minBufferTime="PT75S">', | ||
' <Period id="1" duration="PT30S">', | ||
' <AdaptationSet mimeType="video/mp4">', | ||
' <Representation bandwidth="1">', | ||
' <SegmentBase indexRange="100-200" />', | ||
' </Representation>', | ||
' </AdaptationSet>', | ||
' <AdaptationSet id="1" mimeType="application/mp4">', | ||
' <Representation codecs="stpp">', | ||
' <SegmentTemplate media="1.mp4" duration="1" />', | ||
' </Representation>', | ||
' </AdaptationSet>', | ||
' </Period>', | ||
'</MPD>' | ||
].join('\n'); | ||
fakeNetEngine.setResponseMapAsText({'dummy://foo': manifestText}); | ||
parser.start('dummy://foo', playerInterface) | ||
.then(function(manifest) { | ||
expect(manifest.periods.length).toBe(1); | ||
// In #875, this was an empty list. | ||
expect(manifest.periods[0].textStreams.length).toBe(1); | ||
if (manifest.periods[0].textStreams.length) { | ||
var ContentType = shaka.util.ManifestParserUtils.ContentType; | ||
expect(manifest.periods[0].textStreams[0].type) | ||
.toBe(ContentType.TEXT); | ||
} | ||
}) | ||
.catch(fail) | ||
.then(done); | ||
}); | ||
it('ignores duplicate Representation IDs for VOD', function(done) { | ||
@@ -851,0 +887,0 @@ var source = [ |
@@ -167,2 +167,48 @@ /** | ||
describe('plays', function() { | ||
it('while external text tracks', function(done) { | ||
player.load('test:sintel_no_text_compiled').then(function() { | ||
// For some reason, using path-absolute URLs (i.e. without the hostname) | ||
// like this doesn't work on Safari. So manually resolve the URL. | ||
var locationUri = new goog.Uri(location.href); | ||
var partialUri = new goog.Uri('/base/test/test/assets/text-clip.vtt'); | ||
var absoluteUri = locationUri.resolve(partialUri); | ||
player.addTextTrack(absoluteUri.toString(), 'en', 'subtitles', | ||
'text/vtt'); | ||
video.play(); | ||
return Util.delay(5); | ||
}).then(function() { | ||
var textTracks = player.getTextTracks(); | ||
expect(textTracks).toBeTruthy(); | ||
expect(textTracks.length).toBe(1); | ||
expect(textTracks[0].active).toBe(true); | ||
expect(textTracks[0].language).toEqual('en'); | ||
}).catch(fail).then(done); | ||
}); | ||
it('while changing languages with short Periods', function(done) { | ||
// See: https://github.com/google/shaka-player/issues/797 | ||
player.configure({preferredAudioLanguage: 'en'}); | ||
player.load('test:sintel_short_periods_compiled').then(function() { | ||
video.play(); | ||
return waitUntilPlayheadReaches(video, 8, 30); | ||
}).then(function() { | ||
// The Period changes at 10 seconds. Assert that we are in the previous | ||
// Period and have buffered into the next one. | ||
expect(video.currentTime).toBeLessThan(9); | ||
expect(video.buffered.end(0)).toBeGreaterThan(11); | ||
// Change to a different language; this should clear the buffers and | ||
// cause a Period transition again. | ||
expect(getActiveLanguage()).toBe('en'); | ||
player.selectAudioLanguage('es'); | ||
return waitUntilPlayheadReaches(video, 21, 30); | ||
}).then(function() { | ||
// Should have gotten past the next Period transition and still be | ||
// playing the new language. | ||
expect(getActiveLanguage()).toBe('es'); | ||
}).catch(fail).then(done); | ||
}); | ||
window.shakaAssets.testAssets.forEach(function(asset) { | ||
@@ -244,2 +290,14 @@ if (asset.disabled) return; | ||
}); | ||
/** | ||
* Gets the language of the active Variant. | ||
* @return {string} | ||
*/ | ||
function getActiveLanguage() { | ||
var tracks = player.getVariantTracks().filter(function(t) { | ||
return t.active; | ||
}); | ||
expect(tracks.length).toBeGreaterThan(0); | ||
return tracks[0].language; | ||
} | ||
}); | ||
@@ -246,0 +304,0 @@ |
@@ -206,7 +206,8 @@ /** | ||
var ret = jasmine.createSpyObj('fakeStreamingEngine', [ | ||
'destroy', 'configure', 'init', 'getCurrentPeriod', 'getActiveStreams', | ||
'notifyNewTextStream', 'switch', 'seeked' | ||
'destroy', 'configure', 'init', 'getCurrentPeriod', 'getActivePeriod', | ||
'getActiveStreams', 'notifyNewTextStream', 'switch', 'seeked' | ||
]); | ||
ret.destroy.and.callFake(resolve); | ||
ret.getCurrentPeriod.and.returnValue(null); | ||
ret.getActivePeriod.and.returnValue(null); | ||
ret.getActiveStreams.and.returnValue(activeStreams); | ||
@@ -213,0 +214,0 @@ ret.notifyNewTextStream.and.callFake(resolve); |
@@ -111,2 +111,26 @@ /** | ||
}, | ||
// 'sintel_short_periods' : Generated by createManifests(). | ||
'sintel_no_text': { | ||
video: { | ||
initSegmentUri: '/base/test/test/assets/sintel-video-init.mp4', | ||
mvhdOffset: 0x24, | ||
segmentUri: '/base/test/test/assets/sintel-video-segment.mp4', | ||
tfdtOffset: 0x38, | ||
segmentDuration: 10, | ||
presentationTimeOffset: 0, | ||
mimeType: 'video/mp4', | ||
codecs: 'avc1.42c01e' | ||
}, | ||
audio: { | ||
initSegmentUri: '/base/test/test/assets/sintel-audio-init.mp4', | ||
mvhdOffset: 0x20, | ||
segmentUri: '/base/test/test/assets/sintel-audio-segment.mp4', | ||
tfdtOffset: 0x3c, | ||
segmentDuration: 10.005, | ||
presentationTimeOffset: 0, | ||
mimeType: 'audio/mp4', | ||
codecs: 'mp4a.40.2' | ||
}, | ||
duration: 30 | ||
}, | ||
'sintel-enc': { | ||
@@ -297,11 +321,13 @@ video: { | ||
// This seems to be necessary. Otherwise, we end up with a URL like | ||
// "http:/base/..." which then fails to load on Safari for some reason. | ||
var locationUri = new goog.Uri(location.href); | ||
var partialUri = new goog.Uri(data.text.uri); | ||
var absoluteUri = locationUri.resolve(partialUri); | ||
if (data.text) { | ||
// This seems to be necessary. Otherwise, we end up with a URL like | ||
// "http:/base/..." which then fails to load on Safari for some reason. | ||
var locationUri = new goog.Uri(location.href); | ||
var partialUri = new goog.Uri(data.text.uri); | ||
var absoluteUri = locationUri.resolve(partialUri); | ||
gen.addTextStream(3) | ||
.mime(data.text.mimeType, data.text.codecs) | ||
.textStream(absoluteUri.toString()); | ||
gen.addTextStream(3) | ||
.mime(data.text.mimeType, data.text.codecs) | ||
.textStream(absoluteUri.toString()); | ||
} | ||
@@ -311,2 +337,30 @@ MANIFESTS[name + suffix] = gen.build(); | ||
// Custom generators: | ||
if (true) { // The linter complains without the "if" (i.e. for plain blocks) | ||
var data = DATA['sintel']; | ||
var period_duration = 10; | ||
var num_periods = 10; | ||
var gen = new window.shaka.test.ManifestGenerator(shaka) | ||
.setPresentationDuration(period_duration * num_periods); | ||
for (var i = 0; i < num_periods; i++) { | ||
gen.addPeriod(period_duration * i); | ||
gen.addVariant(2 * i).language('en'); | ||
gen.addVideo(4 * i); | ||
addStreamInfo(gen, data, ContentType.VIDEO, 'sintel'); | ||
gen.addAudio(4 * i + 1); | ||
addStreamInfo(gen, data, ContentType.AUDIO, 'sintel'); | ||
gen.addVariant(2 * i + 1).language('es'); | ||
gen.addVideo(4 * i + 2); | ||
addStreamInfo(gen, data, ContentType.VIDEO, 'sintel'); | ||
gen.addAudio(4 * i + 3); | ||
addStreamInfo(gen, data, ContentType.AUDIO, 'sintel'); | ||
} | ||
MANIFESTS['sintel_short_periods' + suffix] = gen.build(); | ||
} | ||
return Promise.all(async); | ||
@@ -313,0 +367,0 @@ }; |
@@ -162,2 +162,25 @@ /** | ||
it('works even if stage is rejected after being canceled', function(done) { | ||
var p = new shaka.util.PublicPromise(); | ||
var finalComplete = false; | ||
chain.then(function() { | ||
return p; | ||
}).finalize().then(fail).catch(function(err) { | ||
finalComplete = true; | ||
shaka.test.Util.expectToEqualError(cannedError, err); | ||
}); | ||
chain.cancel(cannedError).catch(fail).then(function() { | ||
// Delay so the catch block above can run. | ||
return shaka.test.Util.delay(0.1); | ||
}).then(function() { | ||
expect(finalComplete).toBe(true); | ||
done(); | ||
}); | ||
p.reject(new shaka.util.Error( | ||
shaka.util.Error.Severity.CRITICAL, | ||
shaka.util.Error.Category.MANIFEST, | ||
shaka.util.Error.Code.UNABLE_TO_GUESS_MANIFEST_TYPE)); | ||
}); | ||
it('resolves even after the finalized chain is resolved', function(done) { | ||
@@ -164,0 +187,0 @@ var stageComplete = false; |
@@ -22,2 +22,4 @@ /** | ||
var preferredTextLanguage = 'en'; | ||
var preferredAudioRole = 'main'; | ||
var preferredTextRole = 'main'; | ||
@@ -77,2 +79,23 @@ describe('filterVariantsByRoleAndLanguage', function() { | ||
}); | ||
it('chooses variants in preferred language and role', function() { | ||
manifest = new shaka.test.ManifestGenerator() | ||
.addPeriod(0) | ||
.addVariant(0) | ||
.language('en') | ||
.addAudio(0).roles(['main', 'commentary']) | ||
.addVariant(1) | ||
.language('en') | ||
.addAudio(1).roles(['secondary']) | ||
.addVariant(2) | ||
.language('es') | ||
.addAudio(2).roles(['main']) | ||
.build(); | ||
var chosen = shaka.util.StreamUtils.filterVariantsByRoleAndLanguage( | ||
manifest.periods[0], | ||
preferredAudioLanguage, undefined, preferredAudioRole); | ||
expect(chosen.length).toBe(1); | ||
expect(chosen[0]).toBe(manifest.periods[0].variants[0]); | ||
}); | ||
}); | ||
@@ -115,2 +138,22 @@ | ||
}); | ||
it('chooses text streams in preferred language and role', function() { | ||
manifest = new shaka.test.ManifestGenerator() | ||
.addPeriod(0) | ||
.addTextStream(1) | ||
.language('en') | ||
.roles(['main', 'commentary']) | ||
.addTextStream(2) | ||
.language('es') | ||
.addTextStream(3) | ||
.language('en') | ||
.roles(['caption']) | ||
.build(); | ||
var chosen = shaka.util.StreamUtils.filterTextStreamsByRoleAndLanguage( | ||
manifest.periods[0], | ||
preferredTextLanguage, undefined, preferredTextRole); | ||
expect(chosen.length).toBe(1); | ||
expect(chosen[0]).toBe(manifest.periods[0].textStreams[0]); | ||
}); | ||
}); | ||
@@ -117,0 +160,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
17052735
79772
155