diff --git a/dashboard-ui/scripts/librarybrowser.js b/dashboard-ui/scripts/librarybrowser.js index 511f838559..187e95c02e 100644 --- a/dashboard-ui/scripts/librarybrowser.js +++ b/dashboard-ui/scripts/librarybrowser.js @@ -2482,7 +2482,7 @@ var name = LibraryBrowser.getPosterViewDisplayName(item, true); html += '
'; - var logoHeight = isSmallItem ? 20 : 24; + var logoHeight = isSmallItem ? 20 : 26; var maxLogoWidth = isPortrait ? 100 : 200; var imgUrl; @@ -2556,7 +2556,7 @@ buttonCount++; } - if (currentUser.Configuration.IsAdministrator && item.Type != "Recording") { + if (currentUser.Configuration.IsAdministrator && item.Type != "Recording" && item.Type != "Program") { html += ''; buttonCount++; } diff --git a/dashboard-ui/scripts/mediaplayer.js b/dashboard-ui/scripts/mediaplayer.js index 3cb17610d4..36f9b28d6f 100644 --- a/dashboard-ui/scripts/mediaplayer.js +++ b/dashboard-ui/scripts/mediaplayer.js @@ -4,7 +4,6 @@ var self = this; - var testableAudioElement = document.createElement('audio'); var testableVideoElement = document.createElement('video'); var currentMediaElement; var currentProgressInterval; @@ -214,39 +213,36 @@ var currentSrc = element.currentSrc; currentSrc = replaceQueryString(currentSrc, 'starttimeticks', ticks); - + if (params.AudioStreamIndex != null) { currentSrc = replaceQueryString(currentSrc, 'AudioStreamIndex', params.AudioStreamIndex); } if (params.SubtitleStreamIndex != null) { currentSrc = replaceQueryString(currentSrc, 'SubtitleStreamIndex', params.SubtitleStreamIndex); } - if (params.MaxWidth != null) { - currentSrc = replaceQueryString(currentSrc, 'MaxWidth', params.MaxWidth); - } - if (params.VideoBitrate != null) { - currentSrc = replaceQueryString(currentSrc, 'VideoBitrate', params.VideoBitrate); - } - if (params.AudioBitrate != null) { - currentSrc = replaceQueryString(currentSrc, 'AudioBitrate', params.AudioBitrate); - } - if (params.AudioCodec) { - currentSrc = replaceQueryString(currentSrc, 'AudioCodec', params.AudioCodec); - } - if (params.VideoCodec) { - currentSrc = replaceQueryString(currentSrc, 'VideoCodec', params.VideoCodec); - } - if (params.Static != null) { - currentSrc = replaceQueryString(currentSrc, 'Static', params.Static); + var maxWidth = params.MaxWidth || getParameterByName('MaxWidth', currentSrc); + var audioStreamIndex = params.AudioStreamIndex == null ? getParameterByName('AudioStreamIndex', currentSrc) : params.AudioStreamIndex; + var subtitleStreamIndex = params.SubtitleStreamIndex == null ? getParameterByName('SubtitleStreamIndex', currentSrc) : params.SubtitleStreamIndex; + var videoBitrate = parseInt(getParameterByName('VideoBitrate', currentSrc) || '0'); + var audioBitrate = parseInt(getParameterByName('AudioBitrate', currentSrc) || '0'); + var bitrate = params.Bitrate || (videoBitrate + audioBitrate); - if (params.Static == 'true') { - currentSrc = currentSrc.replace('.webm', '.mp4').replace('.m3u8', '.mp4'); - } else { - currentSrc = currentSrc.replace('.mp4', getTranscodingExtension()); - } + var transcodingExtension = getTranscodingExtension(); + + var finalParams = getFinalVideoParams(currentItem, maxWidth, bitrate, audioStreamIndex, subtitleStreamIndex, transcodingExtension); + currentSrc = replaceQueryString(currentSrc, 'MaxWidth', finalParams.maxWidth); + currentSrc = replaceQueryString(currentSrc, 'VideoBitrate', finalParams.videoBitrate); + currentSrc = replaceQueryString(currentSrc, 'AudioBitrate', finalParams.audioBitrate); + + if (finalParams.isStatic) { + currentSrc = currentSrc.replace('.webm', '.mp4').replace('.m3u8', '.mp4'); + } else { + currentSrc = currentSrc.replace('.mp4', getTranscodingExtension()); } + currentSrc = replaceQueryString(currentSrc, 'Static', finalParams.isStatic); + clearProgressInterval(); $(element).off('ended.playbackstopped').off('ended.playnext').on("play.onceafterseek", function () { @@ -314,7 +310,7 @@ if (!$(this).hasClass('selectedMediaFlyoutOption')) { var index = parseInt(this.getAttribute('data-index')); - changeStream(getCurrentTicks(), { AudioStreamIndex: index, Static: false }); + changeStream(getCurrentTicks(), { AudioStreamIndex: index }); } hideFlyout($('#audioTracksFlyout')); @@ -325,7 +321,7 @@ if (!$(this).hasClass('selectedMediaFlyoutOption')) { var index = parseInt(this.getAttribute('data-index')); - changeStream(getCurrentTicks(), { SubtitleStreamIndex: index, Static: false }); + changeStream(getCurrentTicks(), { SubtitleStreamIndex: index }); } hideFlyout($('#subtitleFlyout')); @@ -336,23 +332,14 @@ if (!$(this).hasClass('selectedMediaFlyoutOption')) { var maxWidth = parseInt(this.getAttribute('data-maxwidth')); - var videoBitrate = parseInt(this.getAttribute('data-videobitrate')); - var audioBitrate = parseInt(this.getAttribute('data-audiobitrate')); + var bitrate = parseInt(this.getAttribute('data-bitrate')); - var audioCodec = this.getAttribute('data-audiocodec'); - var videoCodec = this.getAttribute('data-videocodec'); - var isStatic = this.getAttribute('data-static'); - - localStorage.setItem('preferredVideoBitrate', videoBitrate + audioBitrate); + localStorage.setItem('preferredVideoBitrate', bitrate); changeStream(getCurrentTicks(), { MaxWidth: maxWidth, - VideoBitrate: videoBitrate, - AudioBitrate: audioBitrate, - Static: isStatic, - AudioCodec: audioCodec, - VideoCodec: videoCodec + Bitrate: bitrate }); } @@ -542,20 +529,38 @@ return audioElement[0]; } - function canPlayVideoDirect(item, videoStream) { + function canPlayVideoDirect(item, videoStream, audioStream, subtitleStream, maxWidth, bitrate) { if (item.VideoType != "VideoFile" || item.LocationType != "FileSystem") { + console.log('Transcoding because the content is not a video file'); return false; } + if ((videoStream.Codec || '').toLowerCase().indexOf('h264') == -1) { + console.log('Transcoding because the content is not h264'); return false; } - var audioStreams = (item.MediaStreams || []).filter(function (stream) { - return stream.Type == "Audio"; - }); + if (audioStream && !canPlayAudioStreamDirect(audioStream)) { + console.log('Transcoding because the audio cant be played directly.'); + return false; + } - if (audioStreams.length && !audioStreams.filter(canPlayAudioStreamDirect).length) { + if (subtitleStream) { + console.log('Transcoding because subtitles are required'); + return false; + } + + if (!videoStream.Width || videoStream.Width > maxWidth) { + console.log('Transcoding because resolution is too high'); + return false; + } + + var videoBitrate = videoStream.BitRate || 0; + var audioBitrate = audioStream ? audioStream.BitRate || 0 : null; + + if ((videoBitrate + audioBitrate) > bitrate) { + console.log('Transcoding because bitrate is too high'); return false; } @@ -571,9 +576,11 @@ function canPlayAudioStreamDirect(audioStream) { var audioCodec = (audioStream.Codec || '').toLowerCase().replace('-', ''); + if (audioCodec.indexOf('aac') == -1 && audioCodec.indexOf('mp3') == -1 && audioCodec.indexOf('mpeg') == -1) { + return false; } @@ -589,19 +596,12 @@ return true; } - function getVideoQualityOptions(item, audioStreamIndex, transcodeExtension) { + function getVideoQualityOptions(item) { var videoStream = (item.MediaStreams || []).filter(function (stream) { return stream.Type == "Video"; })[0]; - var audioStreams = (item.MediaStreams || []).filter(function (stream) { - return stream.Type == "Audio"; - }); - - var audioStream = audioStreamIndex == null ? null : audioStreams[audioStreamIndex]; - - var canPlayDirect = canPlayVideoDirect(item, videoStream, audioStream); var bitrateSetting = parseInt(localStorage.getItem('preferredVideoBitrate') || '') || 1500000; var maxAllowedWidth = Math.max(screen.height, screen.width); @@ -614,13 +614,6 @@ maxAllowedWidth = videoStream.Width; } - var canPlayVideoCodecDirect = (videoStream ? videoStream.Codec : '').toLowerCase().indexOf('h264') != -1; - var canPlayAudioDirect = audioStream ? canPlayAudioStreamDirect(audioStream) : false; - - var videoBitrate = videoStream ? videoStream.BitRate : null; - var audioBitrate = audioStream ? audioStream.BitRate : null; - var totalBitrate = (videoBitrate || 0) + (audioBitrate || 0); - // Some 1080- videos are reported as 1912? if (maxAllowedWidth >= 1910) { options.push({ name: '1080p - 10Mbps', maxWidth: 1920, bitrate: 10000000 }); @@ -651,8 +644,6 @@ options.push({ name: '360p', maxWidth: 640, bitrate: 400000 }); options.push({ name: '240p', maxWidth: 426, bitrate: 320000 }); - var videoWidth = videoStream ? videoStream.Width : null; - var i, length, option; var selectedIndex = -1; for (i = 0, length = options.length; i < length; i++) { @@ -662,32 +653,6 @@ if (selectedIndex == -1 && option.bitrate <= bitrateSetting) { selectedIndex = i; } - - option.audioBitrate = option.bitrate >= 700000 ? 128000 : 64000; - - if (canPlayVideoCodecDirect && - totalBitrate && - option.bitrate >= totalBitrate && - videoWidth && - option.maxWidth >= videoWidth) { - - if (canPlayDirect) { - - option.isStatic = true; - option.videoCodec = 'copy'; - option.audioCodec = 'copy'; - } - //else if (canPlayVideoCodecDirect && transcodeExtension == '.m3u8') { - // option.videoCodec = 'copy'; - //} - } - - option.isStatic = option.isStatic || false; - - option.videoCodec = option.videoCodec || (transcodeExtension == '.webm' ? 'vpx' : 'h264'); - option.audioCodec = option.audioCodec || (transcodeExtension == '.webm' ? 'Vorbis' : 'aac'); - - option.videoBitrate = option.bitrate - option.audioBitrate; } if (selectedIndex == -1) { @@ -697,15 +662,40 @@ options[selectedIndex].selected = true; - var firstOption = options[0]; - - if (firstOption.isStatic) { - firstOption.name = 'Direct'; - } - return options; } + function getFinalVideoParams(item, maxWidth, bitrate, audioStreamIndex, subtitleStreamIndex, transcodingExtension) { + + var videoStream = (item.MediaStreams || []).filter(function (stream) { + return stream.Type === "Video"; + })[0]; + + var audioStream = (item.MediaStreams || []).filter(function (stream) { + return stream.Index === audioStreamIndex; + })[0]; + + var subtitleStream = (item.MediaStreams || []).filter(function (stream) { + return stream.Index === subtitleStreamIndex ; + })[0]; + + var canPlayDirect = canPlayVideoDirect(item, videoStream, audioStream, subtitleStream, maxWidth, bitrate); + + var audioBitrate = bitrate >= 700000 ? 128000 : 64000; + + var videoBitrate = bitrate - audioBitrate; + + return { + + isStatic: canPlayDirect, + maxWidth: maxWidth, + audioCodec: transcodingExtension == '.webm' ? 'vorbis' : 'aac', + videoCodec: transcodingExtension == '.webm' ? 'vpx' : 'h264', + audioBitrate: audioBitrate, + videoBitrate: videoBitrate + }; + } + function playVideo(item, startPosition, user) { var mediaStreams = item.MediaStreams || []; @@ -720,17 +710,20 @@ Static: false }; - var mp4Quality = getVideoQualityOptions(item, baseParams.AudioStreamIndex, '.mp4').filter(function (opt) { + var mp4Quality = getVideoQualityOptions(item).filter(function (opt) { return opt.selected; })[0]; + mp4Quality = $.extend(mp4Quality, getFinalVideoParams(item, mp4Quality.maxWidth, mp4Quality.bitrate, baseParams.AudioStreamIndex, baseParams.SubtitleStreamIndex, '.mp4')); - var webmQuality = getVideoQualityOptions(item, baseParams.AudioStreamIndex, '.webm').filter(function (opt) { + var webmQuality = getVideoQualityOptions(item).filter(function (opt) { return opt.selected; })[0]; + webmQuality = $.extend(webmQuality, getFinalVideoParams(item, webmQuality.maxWidth, webmQuality.bitrate, baseParams.AudioStreamIndex, baseParams.SubtitleStreamIndex, '.webm')); - var m3U8Quality = getVideoQualityOptions(item, baseParams.AudioStreamIndex, '.m3u8').filter(function (opt) { + var m3U8Quality = getVideoQualityOptions(item).filter(function (opt) { return opt.selected; })[0]; + m3U8Quality = $.extend(m3U8Quality, getFinalVideoParams(item, mp4Quality.maxWidth, mp4Quality.bitrate, baseParams.AudioStreamIndex, baseParams.SubtitleStreamIndex, '.mp4')); // Webm must be ahead of mp4 due to the issue of mp4 playing too fast in chrome var prioritizeWebmOverH264 = $.browser.chrome || $.browser.msie; @@ -1863,7 +1856,7 @@ cssClass += " selectedMediaFlyoutOption"; } - html += '
'; + html += '
'; html += '
';