mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
use server to build initial stream url's
This commit is contained in:
parent
c64474f046
commit
1fc390dc50
3 changed files with 375 additions and 295 deletions
|
@ -428,14 +428,12 @@
|
|||
|
||||
if (!$(this).hasClass('selectedMediaPopupOption')) {
|
||||
|
||||
var maxWidth = parseInt(this.getAttribute('data-maxwidth'));
|
||||
var bitrate = parseInt(this.getAttribute('data-bitrate'));
|
||||
|
||||
AppSettings.maxStreamingBitrate(bitrate);
|
||||
|
||||
self.changeStream(self.getCurrentTicks(), {
|
||||
|
||||
MaxWidth: maxWidth,
|
||||
Bitrate: bitrate
|
||||
});
|
||||
}
|
||||
|
@ -796,7 +794,12 @@
|
|||
var currentSrc = self.getCurrentSrc(self.currentMediaElement).toLowerCase();
|
||||
var isStatic = currentSrc.indexOf('static=true') != -1;
|
||||
|
||||
var options = getVideoQualityOptions(self.currentMediaSource.MediaStreams);
|
||||
var videoStream = self.currentMediaSource.MediaStreams.filter(function (stream) {
|
||||
return stream.Type == "Video";
|
||||
})[0];
|
||||
var videoWidth = videoStream ? videoStream.Width : null;
|
||||
|
||||
var options = self.getVideoQualityOptions(videoWidth);
|
||||
|
||||
if (isStatic) {
|
||||
options[0].name = "Direct";
|
||||
|
@ -819,7 +822,7 @@
|
|||
cssClass += ' selectedMediaPopupOption';
|
||||
}
|
||||
|
||||
var optionHtml = '<li><a data-maxwidth="' + option.maxWidth + '" data-bitrate="' + option.bitrate + '" class="' + cssClass + '" href="#">';
|
||||
var optionHtml = '<li><a data-bitrate="' + option.bitrate + '" class="' + cssClass + '" href="#">';
|
||||
|
||||
optionHtml += '<p style="margin:0;">';
|
||||
|
||||
|
@ -845,85 +848,6 @@
|
|||
return html;
|
||||
}
|
||||
|
||||
function getVideoQualityOptions(mediaStreams) {
|
||||
|
||||
var videoStream = mediaStreams.filter(function (stream) {
|
||||
return stream.Type == "Video";
|
||||
})[0];
|
||||
|
||||
var bitrateSetting = AppSettings.maxStreamingBitrate();
|
||||
|
||||
var maxAllowedWidth = self.getMaxPlayableWidth();
|
||||
|
||||
var options = [];
|
||||
|
||||
// We have media info
|
||||
if (videoStream && videoStream.Width) {
|
||||
|
||||
maxAllowedWidth = videoStream.Width;
|
||||
}
|
||||
|
||||
// Some 1080- videos are reported as 1912?
|
||||
if (maxAllowedWidth >= 1900) {
|
||||
options.push({ name: '1080p - 30Mbps', maxWidth: 1920, bitrate: 30000000 });
|
||||
options.push({ name: '1080p - 25Mbps', maxWidth: 1920, bitrate: 25000000 });
|
||||
options.push({ name: '1080p - 20Mbps', maxWidth: 1920, bitrate: 20000000 });
|
||||
options.push({ name: '1080p - 15Mbps', maxWidth: 1920, bitrate: 15000000 });
|
||||
options.push({ name: '1080p - 10Mbps', maxWidth: 1920, bitrate: 10000000 });
|
||||
options.push({ name: '1080p - 8Mbps', maxWidth: 1920, bitrate: 8000000 });
|
||||
options.push({ name: '1080p - 6Mbps', maxWidth: 1920, bitrate: 6000000 });
|
||||
options.push({ name: '1080p - 5Mbps', maxWidth: 1920, bitrate: 5000000 });
|
||||
}
|
||||
else if (maxAllowedWidth >= 1260) {
|
||||
options.push({ name: '720p - 10Mbps', maxWidth: 1280, bitrate: 10000000 });
|
||||
options.push({ name: '720p - 8Mbps', maxWidth: 1280, bitrate: 8000000 });
|
||||
options.push({ name: '720p - 6Mbps', maxWidth: 1280, bitrate: 6000000 });
|
||||
options.push({ name: '720p - 5Mbps', maxWidth: 1280, bitrate: 5000000 });
|
||||
}
|
||||
else if (maxAllowedWidth >= 460) {
|
||||
options.push({ name: '480p - 4Mbps', maxWidth: 720, bitrate: 4000000 });
|
||||
options.push({ name: '480p - 3Mbps', maxWidth: 720, bitrate: 3000000 });
|
||||
options.push({ name: '480p - 2.5Mbps', maxWidth: 720, bitrate: 2500000 });
|
||||
options.push({ name: '480p - 2Mbps', maxWidth: 720, bitrate: 2000000 });
|
||||
options.push({ name: '480p - 1.5Mbps', maxWidth: 720, bitrate: 1500000 });
|
||||
}
|
||||
|
||||
if (maxAllowedWidth >= 1260) {
|
||||
options.push({ name: '720p - 4Mbps', maxWidth: 1280, bitrate: 4000000 });
|
||||
options.push({ name: '720p - 3Mbps', maxWidth: 1280, bitrate: 3000000 });
|
||||
options.push({ name: '720p - 2Mbps', maxWidth: 1280, bitrate: 2000000 });
|
||||
|
||||
// The extra 1 is because they're keyed off the bitrate value
|
||||
options.push({ name: '720p - 1Mbps', maxWidth: 1280, bitrate: 1000001 });
|
||||
}
|
||||
|
||||
options.push({ name: '480p - 1.0Mbps', maxWidth: 720, bitrate: 1000000 });
|
||||
options.push({ name: '480p - 720kbps', maxWidth: 720, bitrate: 720000 });
|
||||
options.push({ name: '480p - 420kbps', maxWidth: 720, bitrate: 420000 });
|
||||
options.push({ name: '360p', maxWidth: 640, bitrate: 400000 });
|
||||
options.push({ name: '240p', maxWidth: 426, bitrate: 320000 });
|
||||
|
||||
var i, length, option;
|
||||
var selectedIndex = -1;
|
||||
for (i = 0, length = options.length; i < length; i++) {
|
||||
|
||||
option = options[i];
|
||||
|
||||
if (selectedIndex == -1 && option.bitrate <= bitrateSetting) {
|
||||
selectedIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedIndex == -1) {
|
||||
|
||||
selectedIndex = options.length - 1;
|
||||
}
|
||||
|
||||
options[selectedIndex].selected = true;
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
function bindEventsForPlayback() {
|
||||
|
||||
var hideElementsOnIdle = !$.browser.mobile;
|
||||
|
@ -1007,101 +931,71 @@
|
|||
}
|
||||
};
|
||||
|
||||
self.playVideo = function (playbackInfo, item, mediaSource, startPosition) {
|
||||
self.playVideo = function (deviceProfile, playbackInfo, item, mediaSource, startPosition) {
|
||||
|
||||
var videoUrl;
|
||||
var contentType;
|
||||
|
||||
var mediaStreams = mediaSource.MediaStreams || [];
|
||||
|
||||
var subtitleStreams = mediaStreams.filter(function (s) {
|
||||
return s.Type == 'Subtitle';
|
||||
});
|
||||
|
||||
var selectedSubtitleStream = subtitleStreams.filter(function (s) {
|
||||
return s.Index == mediaSource.DefaultSubtitleStreamIndex;
|
||||
|
||||
})[0];
|
||||
|
||||
var baseParams = {
|
||||
audioChannels: 2,
|
||||
StartTimeTicks: startPosition,
|
||||
AudioStreamIndex: mediaSource.DefaultAudioStreamIndex,
|
||||
deviceId: ApiClient.deviceId(),
|
||||
Static: false,
|
||||
mediaSourceId: mediaSource.Id,
|
||||
api_key: ApiClient.accessToken(),
|
||||
StreamId: playbackInfo.StreamId
|
||||
};
|
||||
|
||||
if (selectedSubtitleStream && (!self.supportsSubtitleStreamExternally(selectedSubtitleStream) || !self.supportsTextTracks())) {
|
||||
baseParams.SubtitleStreamIndex = mediaSource.DefaultSubtitleStreamIndex;
|
||||
}
|
||||
|
||||
var mp4Quality = getVideoQualityOptions(mediaStreams).filter(function (opt) {
|
||||
return opt.selected;
|
||||
})[0];
|
||||
mp4Quality = $.extend(mp4Quality, self.getFinalVideoParams(mediaSource, mp4Quality.maxWidth, mp4Quality.bitrate, baseParams.AudioStreamIndex, baseParams.SubtitleStreamIndex, '.mp4'));
|
||||
|
||||
var webmQuality = getVideoQualityOptions(mediaStreams).filter(function (opt) {
|
||||
return opt.selected;
|
||||
})[0];
|
||||
webmQuality = $.extend(webmQuality, self.getFinalVideoParams(mediaSource, webmQuality.maxWidth, webmQuality.bitrate, baseParams.AudioStreamIndex, baseParams.SubtitleStreamIndex, '.webm'));
|
||||
|
||||
var m3U8Quality = getVideoQualityOptions(mediaStreams).filter(function (opt) {
|
||||
return opt.selected;
|
||||
})[0];
|
||||
m3U8Quality = $.extend(m3U8Quality, self.getFinalVideoParams(mediaSource, mp4Quality.maxWidth, mp4Quality.bitrate, baseParams.AudioStreamIndex, baseParams.SubtitleStreamIndex, '.mp4'));
|
||||
|
||||
var isStatic = mp4Quality.isStatic;
|
||||
|
||||
self.startTimeTicksOffset = isStatic ? 0 : startPosition || 0;
|
||||
|
||||
var startPositionInSeekParam = startPosition ? (startPosition / 10000000) : 0;
|
||||
var seekParam = startPositionInSeekParam ? '#t=' + startPositionInSeekParam : '';
|
||||
|
||||
var mp4VideoUrl = ApiClient.getUrl('Videos/' + item.Id + '/stream.mp4', $.extend({}, baseParams, {
|
||||
Static: isStatic,
|
||||
maxWidth: mp4Quality.maxWidth,
|
||||
videoBitrate: mp4Quality.videoBitrate,
|
||||
audioBitrate: mp4Quality.audioBitrate,
|
||||
VideoCodec: mp4Quality.videoCodec,
|
||||
AudioCodec: mp4Quality.audioCodec,
|
||||
profile: 'high',
|
||||
//EnableAutoStreamCopy: false,
|
||||
level: '41'
|
||||
}));
|
||||
|
||||
if (isStatic && mediaSource.Protocol == 'Http' && !mediaSource.RequiredHttpHeaders.length) {
|
||||
mp4VideoUrl = mediaSource.Path;
|
||||
}
|
||||
|
||||
if (isStatic) {
|
||||
mp4VideoUrl += seekParam;
|
||||
if (mediaSource.enableDirectPlay) {
|
||||
videoUrl = mediaSource.Path;
|
||||
self.startTimeTicksOffset = 0;
|
||||
contentType = 'video/' + mediaSource.Container;
|
||||
} else {
|
||||
mp4VideoUrl += "&StreamId=" + new Date().getTime();
|
||||
|
||||
var selectedSubtitleStream = subtitleStreams.filter(function (s) {
|
||||
return s.Index == mediaSource.DefaultSubtitleStreamIndex;
|
||||
|
||||
})[0];
|
||||
|
||||
var transcodingParams = {
|
||||
audioChannels: 2,
|
||||
StartTimeTicks: startPosition,
|
||||
AudioStreamIndex: mediaSource.DefaultAudioStreamIndex,
|
||||
deviceId: ApiClient.deviceId(),
|
||||
mediaSourceId: mediaSource.Id,
|
||||
api_key: ApiClient.accessToken(),
|
||||
StreamId: playbackInfo.StreamId,
|
||||
ClientTime: new Date().getTime()
|
||||
};
|
||||
|
||||
if (selectedSubtitleStream && (!self.supportsSubtitleStreamExternally(selectedSubtitleStream) || !self.supportsTextTracks())) {
|
||||
transcodingParams.SubtitleStreamIndex = mediaSource.DefaultSubtitleStreamIndex;
|
||||
}
|
||||
|
||||
self.startTimeTicksOffset = mediaSource.SupportsDirectStream ? 0 : startPosition || 0;
|
||||
var startPositionInSeekParam = startPosition ? (startPosition / 10000000) : 0;
|
||||
var seekParam = startPositionInSeekParam ? '#t=' + startPositionInSeekParam : '';
|
||||
|
||||
if (mediaSource.SupportsDirectStream) {
|
||||
|
||||
videoUrl = ApiClient.getUrl('Videos/' + item.Id + '/stream.' + mediaSource.Container, {
|
||||
Static: true,
|
||||
mediaSourceId: mediaSource.Id,
|
||||
api_key: ApiClient.accessToken()
|
||||
});
|
||||
videoUrl += seekParam;
|
||||
contentType = 'video/' + mediaSource.Container;
|
||||
|
||||
} else {
|
||||
videoUrl = ApiClient.getUrl(mediaSource.TranscodingUrl);
|
||||
|
||||
if (mediaSource.TranscodingSubProtocol == 'hls') {
|
||||
|
||||
videoUrl += seekParam;
|
||||
contentType = 'application/x-mpegURL';
|
||||
}
|
||||
else {
|
||||
|
||||
contentType = 'video/' + mediaSource.TranscodingContainer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var webmVideoUrl = ApiClient.getUrl('Videos/' + item.Id + '/stream.webm', $.extend({}, baseParams, {
|
||||
VideoCodec: 'vpx',
|
||||
AudioCodec: 'Vorbis',
|
||||
maxWidth: webmQuality.maxWidth,
|
||||
videoBitrate: webmQuality.videoBitrate,
|
||||
audioBitrate: webmQuality.audioBitrate,
|
||||
EnableAutoStreamCopy: false,
|
||||
StreamId: new Date().getTime()
|
||||
}));
|
||||
|
||||
var hlsVideoUrl = ApiClient.getUrl('Videos/' + item.Id + '/master.m3u8', $.extend({}, baseParams, {
|
||||
maxWidth: m3U8Quality.maxWidth,
|
||||
videoBitrate: m3U8Quality.videoBitrate,
|
||||
audioBitrate: m3U8Quality.audioBitrate,
|
||||
VideoCodec: m3U8Quality.videoCodec,
|
||||
AudioCodec: m3U8Quality.audioCodec,
|
||||
profile: 'high',
|
||||
level: '41',
|
||||
StartTimeTicks: 0,
|
||||
StreamId: new Date().getTime()
|
||||
|
||||
})) + seekParam;
|
||||
|
||||
//======================================================================================>
|
||||
|
||||
// Create video player
|
||||
|
@ -1118,27 +1012,7 @@
|
|||
html += '<video class="itemVideo" id="itemVideo" preload="metadata" autoplay>';
|
||||
}
|
||||
|
||||
if (!isStatic) {
|
||||
// HLS must be at the top for safari
|
||||
html += '<source type="application/x-mpegURL" src="' + hlsVideoUrl + '" />';
|
||||
}
|
||||
|
||||
var mp4BeforeWebm = self.getVideoTranscodingExtension() != '.webm';
|
||||
|
||||
if (mp4BeforeWebm) {
|
||||
html += '<source type="video/mp4" src="' + mp4VideoUrl + '" />';
|
||||
}
|
||||
|
||||
// Have to put webm ahead of mp4 because it will play in fast forward in chrome
|
||||
// And firefox doesn't like fragmented mp4
|
||||
if (!isStatic) {
|
||||
|
||||
html += '<source type="video/webm" src="' + webmVideoUrl + '" />';
|
||||
}
|
||||
|
||||
if (!mp4BeforeWebm) {
|
||||
html += '<source type="video/mp4" src="' + mp4VideoUrl + '" />';
|
||||
}
|
||||
html += '<source type="' + contentType + '" src="' + videoUrl + '" />';
|
||||
|
||||
if (self.supportsTextTracks()) {
|
||||
var textStreams = subtitleStreams.filter(function (s) {
|
||||
|
|
|
@ -37,6 +37,245 @@
|
|||
return targets;
|
||||
};
|
||||
|
||||
var supportsAac = document.createElement('audio').canPlayType('audio/aac').replace(/no/, '');
|
||||
|
||||
self.getVideoQualityOptions = function (videoWidth) {
|
||||
|
||||
var bitrateSetting = AppSettings.maxStreamingBitrate();
|
||||
|
||||
var maxAllowedWidth = videoWidth || 4096;
|
||||
|
||||
var options = [];
|
||||
|
||||
// Some 1080- videos are reported as 1912?
|
||||
if (maxAllowedWidth >= 1900) {
|
||||
options.push({ name: '1080p - 30Mbps', maxWidth: 1920, bitrate: 30000000 });
|
||||
options.push({ name: '1080p - 25Mbps', maxWidth: 1920, bitrate: 25000000 });
|
||||
options.push({ name: '1080p - 20Mbps', maxWidth: 1920, bitrate: 20000000 });
|
||||
options.push({ name: '1080p - 15Mbps', maxWidth: 1920, bitrate: 15000000 });
|
||||
options.push({ name: '1080p - 10Mbps', maxWidth: 1920, bitrate: 10000000 });
|
||||
options.push({ name: '1080p - 8Mbps', maxWidth: 1920, bitrate: 8000000 });
|
||||
options.push({ name: '1080p - 6Mbps', maxWidth: 1920, bitrate: 6000000 });
|
||||
options.push({ name: '1080p - 5Mbps', maxWidth: 1920, bitrate: 5000000 });
|
||||
} else if (maxAllowedWidth >= 1260) {
|
||||
options.push({ name: '720p - 10Mbps', maxWidth: 1280, bitrate: 10000000 });
|
||||
options.push({ name: '720p - 8Mbps', maxWidth: 1280, bitrate: 8000000 });
|
||||
options.push({ name: '720p - 6Mbps', maxWidth: 1280, bitrate: 6000000 });
|
||||
options.push({ name: '720p - 5Mbps', maxWidth: 1280, bitrate: 5000000 });
|
||||
} else if (maxAllowedWidth >= 460) {
|
||||
options.push({ name: '480p - 4Mbps', maxWidth: 720, bitrate: 4000000 });
|
||||
options.push({ name: '480p - 3Mbps', maxWidth: 720, bitrate: 3000000 });
|
||||
options.push({ name: '480p - 2.5Mbps', maxWidth: 720, bitrate: 2500000 });
|
||||
options.push({ name: '480p - 2Mbps', maxWidth: 720, bitrate: 2000000 });
|
||||
options.push({ name: '480p - 1.5Mbps', maxWidth: 720, bitrate: 1500000 });
|
||||
}
|
||||
|
||||
if (maxAllowedWidth >= 1260) {
|
||||
options.push({ name: '720p - 4Mbps', maxWidth: 1280, bitrate: 4000000 });
|
||||
options.push({ name: '720p - 3Mbps', maxWidth: 1280, bitrate: 3000000 });
|
||||
options.push({ name: '720p - 2Mbps', maxWidth: 1280, bitrate: 2000000 });
|
||||
|
||||
// The extra 1 is because they're keyed off the bitrate value
|
||||
options.push({ name: '720p - 1Mbps', maxWidth: 1280, bitrate: 1000001 });
|
||||
}
|
||||
|
||||
options.push({ name: '480p - 1.0Mbps', maxWidth: 720, bitrate: 1000000 });
|
||||
options.push({ name: '480p - 720kbps', maxWidth: 720, bitrate: 720000 });
|
||||
options.push({ name: '480p - 420kbps', maxWidth: 720, bitrate: 420000 });
|
||||
options.push({ name: '360p', maxWidth: 640, bitrate: 400000 });
|
||||
options.push({ name: '240p', maxWidth: 426, bitrate: 320000 });
|
||||
|
||||
var i, length, option;
|
||||
var selectedIndex = -1;
|
||||
for (i = 0, length = options.length; i < length; i++) {
|
||||
|
||||
option = options[i];
|
||||
|
||||
if (selectedIndex == -1 && option.bitrate <= bitrateSetting) {
|
||||
selectedIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedIndex == -1) {
|
||||
|
||||
selectedIndex = options.length - 1;
|
||||
}
|
||||
|
||||
options[selectedIndex].selected = true;
|
||||
|
||||
return options;
|
||||
};
|
||||
|
||||
self.getDeviceProfile = function () {
|
||||
|
||||
var qualityOption = self.getVideoQualityOptions().filter(function (q) {
|
||||
return q.selected;
|
||||
})[0];
|
||||
|
||||
var bitrateSetting = AppSettings.maxStreamingBitrate();
|
||||
|
||||
var profile = {};
|
||||
|
||||
profile.MaxStreamingBitrate = bitrateSetting;
|
||||
profile.MaxStaticBitrate = 40000000;
|
||||
profile.MusicStreamingTranscodingBitrate = 128000;
|
||||
|
||||
profile.DirectPlayProfiles = [];
|
||||
profile.DirectPlayProfiles.push({
|
||||
Container: 'mp4',
|
||||
Type: 'Video',
|
||||
VideoCodec: 'h264',
|
||||
AudioCodec: 'aac,mp3'
|
||||
});
|
||||
|
||||
if ($.browser.chrome) {
|
||||
profile.DirectPlayProfiles.push({
|
||||
Container: 'mkv,m4v',
|
||||
Type: 'Video',
|
||||
VideoCodec: 'h264',
|
||||
AudioCodec: 'aac,mp3'
|
||||
});
|
||||
profile.DirectPlayProfiles.push({
|
||||
Container: 'flv',
|
||||
Type: 'Video'
|
||||
});
|
||||
}
|
||||
|
||||
profile.DirectPlayProfiles.push({
|
||||
Container: 'mp3',
|
||||
Type: 'Audio'
|
||||
});
|
||||
|
||||
if (supportsAac) {
|
||||
profile.DirectPlayProfiles.push({
|
||||
Container: 'aac',
|
||||
Type: 'Audio'
|
||||
});
|
||||
}
|
||||
|
||||
profile.TranscodingProfiles = [];
|
||||
profile.TranscodingProfiles.push({
|
||||
Container: 'mp3',
|
||||
Type: 'Audio',
|
||||
AudioCodec: 'mp3',
|
||||
Context: 'Streaming',
|
||||
Protocol: 'http'
|
||||
});
|
||||
|
||||
if (self.canPlayHls()) {
|
||||
profile.TranscodingProfiles.push({
|
||||
Container: 'ts',
|
||||
Type: 'Video',
|
||||
AudioCodec: 'aac',
|
||||
VideoCodec: 'h264',
|
||||
Context: 'Streaming',
|
||||
Protocol: 'hls'
|
||||
});
|
||||
}
|
||||
|
||||
if (self.canPlayWebm()) {
|
||||
|
||||
profile.TranscodingProfiles.push({
|
||||
Container: 'webm',
|
||||
Type: 'Video',
|
||||
AudioCodec: 'vorbis',
|
||||
VideoCodec: 'vpx',
|
||||
Context: 'Streaming',
|
||||
Protocol: 'http'
|
||||
});
|
||||
}
|
||||
|
||||
profile.TranscodingProfiles.push({
|
||||
Container: 'mp4',
|
||||
Type: 'Video',
|
||||
AudioCodec: 'aac',
|
||||
VideoCodec: 'h264',
|
||||
Context: 'Streaming',
|
||||
Protocol: 'http'
|
||||
});
|
||||
|
||||
profile.ContainerProfiles = [];
|
||||
|
||||
var audioConditions = [];
|
||||
if ($.browser.msie) {
|
||||
audioConditions.push({
|
||||
Condition: 'LessThanEqual',
|
||||
Property: 'AudioChannels',
|
||||
Value: '2'
|
||||
});
|
||||
}
|
||||
|
||||
profile.CodecProfiles = [];
|
||||
profile.CodecProfiles.push({
|
||||
Type: 'Audio',
|
||||
Conditions: audioConditions
|
||||
});
|
||||
|
||||
profile.CodecProfiles.push({
|
||||
Type: 'VideoAudio',
|
||||
Conditions: audioConditions
|
||||
});
|
||||
|
||||
profile.CodecProfiles.push({
|
||||
Type: 'Video',
|
||||
Codec: 'h264',
|
||||
Conditions: [
|
||||
{
|
||||
Condition: 'Equals',
|
||||
Property: 'IsCabac',
|
||||
Value: 'true'
|
||||
},
|
||||
{
|
||||
Condition: 'NotEquals',
|
||||
Property: 'IsAnamorphic',
|
||||
Value: 'true'
|
||||
},
|
||||
{
|
||||
Condition: 'EqualsAny',
|
||||
Property: 'VideoProfile',
|
||||
Value: 'high|main|baseline|constrained baseline'
|
||||
},
|
||||
{
|
||||
Condition: 'LessThanEqual',
|
||||
Property: 'VideoLevel',
|
||||
Value: '41'
|
||||
},
|
||||
{
|
||||
Condition: 'LessThanEqual',
|
||||
Property: 'Width',
|
||||
Value: qualityOption.maxWidth
|
||||
}]
|
||||
});
|
||||
|
||||
profile.CodecProfiles.push({
|
||||
Type: 'Video',
|
||||
Codec: 'vpx',
|
||||
Conditions: [
|
||||
{
|
||||
Condition: 'NotEquals',
|
||||
Property: 'IsAnamorphic',
|
||||
Value: 'true'
|
||||
},
|
||||
{
|
||||
Condition: 'LessThanEqual',
|
||||
Property: 'Width',
|
||||
Value: qualityOption.maxWidth
|
||||
}]
|
||||
});
|
||||
|
||||
// Subtitle profiles
|
||||
// External vtt or burn in
|
||||
profile.SubtitleProfiles = [];
|
||||
if (self.supportsTextTracks()) {
|
||||
profile.SubtitleProfiles.push({
|
||||
Format: 'vtt',
|
||||
Method: 'External'
|
||||
});
|
||||
}
|
||||
|
||||
return profile;
|
||||
};
|
||||
|
||||
self.updateCanClientSeek = function (elem) {
|
||||
|
||||
var duration = elem.duration;
|
||||
|
@ -89,17 +328,27 @@
|
|||
return currentSrc.substring(currentSrc.lastIndexOf('.'));
|
||||
};
|
||||
|
||||
self.getVideoTranscodingExtension = function (currentSrc) {
|
||||
|
||||
if (currentSrc) {
|
||||
return self.getCurrentMediaExtension(currentSrc);
|
||||
}
|
||||
self.canPlayHls = function () {
|
||||
|
||||
var media = testableVideoElement;
|
||||
|
||||
// safari
|
||||
if (media.canPlayType('application/x-mpegURL').replace(/no/, '') ||
|
||||
media.canPlayType('application/vnd.apple.mpegURL').replace(/no/, '')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
self.getVideoTranscodingExtension = function (currentSrc) {
|
||||
|
||||
if (currentSrc) {
|
||||
return self.getCurrentMediaExtension(currentSrc);
|
||||
}
|
||||
|
||||
// safari
|
||||
if (self.canPlayHls()) {
|
||||
return '.m3u8';
|
||||
}
|
||||
|
||||
|
@ -151,7 +400,6 @@
|
|||
currentSrc = replaceQueryString(currentSrc, 'SubtitleStreamIndex', (params.SubtitleStreamIndex == -1 ? '' : params.SubtitleStreamIndex));
|
||||
}
|
||||
|
||||
var maxWidth = params.MaxWidth || getParameterByName('MaxWidth', currentSrc);
|
||||
var audioStreamIndex = params.AudioStreamIndex == null ? getParameterByName('AudioStreamIndex', currentSrc) : params.AudioStreamIndex;
|
||||
if (typeof (audioStreamIndex) == 'string') {
|
||||
audioStreamIndex = parseInt(audioStreamIndex);
|
||||
|
@ -161,9 +409,8 @@
|
|||
var audioBitrate = parseInt(getParameterByName('AudioBitrate', currentSrc) || '0');
|
||||
var bitrate = params.Bitrate || (videoBitrate + audioBitrate);
|
||||
|
||||
var finalParams = self.getFinalVideoParams(self.currentMediaSource, maxWidth, bitrate, audioStreamIndex, subtitleStreamIndex, transcodingExtension);
|
||||
var finalParams = self.getFinalVideoParams(self.currentMediaSource, bitrate, audioStreamIndex, subtitleStreamIndex, transcodingExtension);
|
||||
|
||||
currentSrc = replaceQueryString(currentSrc, 'MaxWidth', finalParams.maxWidth);
|
||||
currentSrc = replaceQueryString(currentSrc, 'VideoBitrate', finalParams.videoBitrate);
|
||||
|
||||
currentSrc = replaceQueryString(currentSrc, 'VideoCodec', finalParams.videoCodec);
|
||||
|
@ -274,7 +521,7 @@
|
|||
return supportsTextTracks;
|
||||
};
|
||||
|
||||
self.getVideoDirectPlayMethod = function (mediaSource, videoStream, audioStream, subtitleStream, maxWidth, bitrate) {
|
||||
self.getVideoDirectPlayMethod = function (mediaSource, videoStream, audioStream, subtitleStream, bitrate) {
|
||||
|
||||
if (!mediaSource) {
|
||||
throw new Error('Null mediaSource');
|
||||
|
@ -322,11 +569,6 @@
|
|||
return null;
|
||||
}
|
||||
|
||||
if (videoStream.Width > maxWidth) {
|
||||
console.log('Transcoding because resolution is too high');
|
||||
return null;
|
||||
}
|
||||
|
||||
if (videoStream && videoStream.IsAnamorphic) {
|
||||
console.log('Transcoding because video is anamorphic');
|
||||
return null;
|
||||
|
@ -372,7 +614,7 @@
|
|||
return null;
|
||||
};
|
||||
|
||||
self.getFinalVideoParams = function (mediaSource, maxWidth, bitrate, audioStreamIndex, subtitleStreamIndex, transcodingExtension) {
|
||||
self.getFinalVideoParams = function (mediaSource, bitrate, audioStreamIndex, subtitleStreamIndex, transcodingExtension) {
|
||||
|
||||
var mediaStreams = mediaSource.MediaStreams;
|
||||
|
||||
|
@ -388,7 +630,7 @@
|
|||
return stream.Index === subtitleStreamIndex && stream.Type == 'Subtitle';
|
||||
})[0];
|
||||
|
||||
var directPlayMethod = self.getVideoDirectPlayMethod(mediaSource, videoStream, audioStream, subtitleStream, maxWidth, bitrate);
|
||||
var directPlayMethod = self.getVideoDirectPlayMethod(mediaSource, videoStream, audioStream, subtitleStream, bitrate);
|
||||
|
||||
var audioBitrate = bitrate >= 700000 ? 192000 : 64000;
|
||||
|
||||
|
@ -396,7 +638,6 @@
|
|||
|
||||
var params = {
|
||||
isStatic: directPlayMethod != null,
|
||||
maxWidth: maxWidth,
|
||||
audioCodec: transcodingExtension == '.webm' ? 'vorbis' : 'aac',
|
||||
videoCodec: transcodingExtension == '.webm' ? 'vpx' : 'h264',
|
||||
audioBitrate: audioBitrate,
|
||||
|
@ -504,11 +745,6 @@
|
|||
|
||||
};
|
||||
|
||||
self.getMaxPlayableWidth = function () {
|
||||
|
||||
return Math.max(screen.height, screen.width);
|
||||
};
|
||||
|
||||
self.playWithIntros = function (items, options, user) {
|
||||
|
||||
var firstItem = items[0];
|
||||
|
@ -534,38 +770,18 @@
|
|||
|
||||
function getOptimalMediaSource(mediaType, versions) {
|
||||
|
||||
var optimalVersion;
|
||||
var bitrateSetting = AppSettings.maxStreamingBitrate();
|
||||
var optimalVersion = versions.filter(function (v) {
|
||||
|
||||
if (mediaType == 'Video') {
|
||||
v.enableDirectPlay = v.SupportsDirectPlay && v.Protocol == 'Http' && !v.RequiredHttpHeaders.length;
|
||||
|
||||
var maxAllowedWidth = self.getMaxPlayableWidth();
|
||||
return v.enableDirectPlay;
|
||||
|
||||
})[0];
|
||||
|
||||
if (!optimalVersion) {
|
||||
optimalVersion = versions.filter(function (v) {
|
||||
|
||||
var videoStream = v.MediaStreams.filter(function (s) {
|
||||
return s.Type == 'Video';
|
||||
})[0];
|
||||
|
||||
var audioStream = v.MediaStreams.filter(function (s) {
|
||||
return s.Type == 'Audio';
|
||||
})[0];
|
||||
|
||||
var directPlayMethod = self.getVideoDirectPlayMethod(v, videoStream, audioStream, null, maxAllowedWidth, bitrateSetting);
|
||||
|
||||
if (directPlayMethod == 'DirectPlay') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return v.SupportsDirectStream && directPlayMethod == 'DirectStream';
|
||||
|
||||
})[0];
|
||||
|
||||
} else {
|
||||
|
||||
optimalVersion = versions.filter(function (v) {
|
||||
|
||||
return v.SupportsDirectStream && canPlayAudioMediaSourceDirect(v);
|
||||
return v.SupportsDirectStream;
|
||||
|
||||
})[0];
|
||||
}
|
||||
|
@ -595,11 +811,22 @@
|
|||
}
|
||||
|
||||
var mediaSource;
|
||||
var deviceProfile = self.getDeviceProfile();
|
||||
|
||||
ApiClient.getJSON(ApiClient.getUrl('Items/' + item.Id + '/PlaybackInfo', {
|
||||
userId: Dashboard.getCurrentUserId()
|
||||
ApiClient.ajax({
|
||||
|
||||
})).done(function (result) {
|
||||
url: ApiClient.getUrl('Items/' + item.Id + '/PlaybackInfo', {
|
||||
userId: Dashboard.getCurrentUserId()
|
||||
|
||||
}),
|
||||
type: 'POST',
|
||||
data: JSON.stringify({
|
||||
DeviceProfile: deviceProfile
|
||||
}),
|
||||
contentType: "application/json",
|
||||
dataType: "json"
|
||||
|
||||
}).done(function (result) {
|
||||
|
||||
if (validatePlaybackInfoResult(result)) {
|
||||
|
||||
|
@ -612,7 +839,7 @@
|
|||
|
||||
if (item.MediaType === "Video") {
|
||||
|
||||
self.currentMediaElement = self.playVideo(result, item, self.currentMediaSource, startPosition);
|
||||
self.currentMediaElement = self.playVideo(deviceProfile, result, item, self.currentMediaSource, startPosition);
|
||||
self.currentDurationTicks = self.currentMediaSource.RunTimeTicks;
|
||||
|
||||
self.updateNowPlayingInfo(item);
|
||||
|
@ -1380,63 +1607,36 @@
|
|||
return $('.mediaPlayerAudio');
|
||||
}
|
||||
|
||||
function canPlayAudioMediaSourceDirect(mediaSource) {
|
||||
|
||||
var sourceContainer = (mediaSource.Container || '').toLowerCase();
|
||||
|
||||
if (sourceContainer == 'mp3' ||
|
||||
(sourceContainer == 'aac' && supportsAac)) {
|
||||
|
||||
for (var i = 0, length = mediaSource.MediaStreams.length; i < length; i++) {
|
||||
|
||||
var stream = mediaSource.MediaStreams[i];
|
||||
|
||||
if (stream.Type == "Audio") {
|
||||
|
||||
// Stream statically when possible
|
||||
if (stream.BitRate <= 320000) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
var supportsAac = document.createElement('audio').canPlayType('audio/aac').replace(/no/, '');
|
||||
|
||||
function playAudio(playbackInfo, item, mediaSource, startPositionTicks) {
|
||||
|
||||
startPositionTicks = startPositionTicks || 0;
|
||||
var audioUrl;
|
||||
if (mediaSource.enableDirectPlay) {
|
||||
|
||||
var baseParams = {
|
||||
audioChannels: 2,
|
||||
audioBitrate: 128000,
|
||||
StartTimeTicks: startPositionTicks,
|
||||
mediaSourceId: mediaSource.Id,
|
||||
deviceId: ApiClient.deviceId(),
|
||||
api_key: ApiClient.accessToken(),
|
||||
StreamId: playbackInfo.StreamId
|
||||
};
|
||||
audioUrl = mediaSource.Path;
|
||||
self.startTimeTicksOffset = 0;
|
||||
|
||||
var sourceContainer = (mediaSource.Container || '').toLowerCase();
|
||||
var isStatic = canPlayAudioMediaSourceDirect(mediaSource);
|
||||
|
||||
var outputContainer = isStatic ? sourceContainer : 'mp3';
|
||||
var audioUrl = ApiClient.getUrl('Audio/' + item.Id + '/stream.' + outputContainer, $.extend({}, baseParams, {
|
||||
audioCodec: outputContainer
|
||||
}));
|
||||
|
||||
if (isStatic) {
|
||||
var seekParam = startPositionTicks ? '#t=' + (startPositionTicks / 10000000) : '';
|
||||
audioUrl += "&static=true" + seekParam;
|
||||
} else {
|
||||
audioUrl += "&ClientTime=" + new Date().getTime();
|
||||
}
|
||||
|
||||
self.startTimeTicksOffset = isStatic ? 0 : startPositionTicks;
|
||||
var isDirectStream = mediaSource.SupportsDirectStream;
|
||||
startPositionTicks = startPositionTicks || 0;
|
||||
|
||||
if (isDirectStream) {
|
||||
|
||||
var outputContainer = (mediaSource.Container || '').toLowerCase();
|
||||
|
||||
var seekParam = startPositionTicks ? '#t=' + (startPositionTicks / 10000000) : '';
|
||||
audioUrl = ApiClient.getUrl('Audio/' + item.Id + '/stream.' + outputContainer, {
|
||||
mediaSourceId: mediaSource.Id,
|
||||
deviceId: ApiClient.deviceId(),
|
||||
api_key: ApiClient.accessToken()
|
||||
});
|
||||
audioUrl += "&static=true" + seekParam;
|
||||
} else {
|
||||
audioUrl = ApiClient.getUrl(mediaSource.TranscodingUrl);
|
||||
}
|
||||
|
||||
self.startTimeTicksOffset = isDirectStream ? 0 : startPositionTicks;
|
||||
}
|
||||
|
||||
var initialVolume = self.getSavedVolume();
|
||||
|
||||
|
|
|
@ -306,7 +306,13 @@
|
|||
throw new Error("Url name cannot be empty");
|
||||
}
|
||||
|
||||
var url = serverAddress + "/" + name;
|
||||
var url = serverAddress;
|
||||
|
||||
if (name.charAt(0) != '/') {
|
||||
url += '/';
|
||||
}
|
||||
|
||||
url += name;
|
||||
|
||||
if (params) {
|
||||
url += "?" + AjaxApi.param(params);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue