diff options
author | yoshiki@chromium.org <yoshiki@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-03-25 01:04:19 +0000 |
---|---|---|
committer | yoshiki@chromium.org <yoshiki@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-03-25 01:04:19 +0000 |
commit | 440a5749ece837355fef9b77fd460ad14e6ffcc6 (patch) | |
tree | d3d7d021bbecc84e96563d080d94061bc10c70f1 | |
parent | 43c1e90b5628aa2d9e7f82523002e2d439109289 (diff) | |
download | chromium_src-440a5749ece837355fef9b77fd460ad14e6ffcc6.zip chromium_src-440a5749ece837355fef9b77fd460ad14e6ffcc6.tar.gz chromium_src-440a5749ece837355fef9b77fd460ad14e6ffcc6.tar.bz2 |
[VideoPlayer] dedicated video player app
This commit adds a dedicated video player app. This is almost copy of the embedded video player app in Files.app. The embedded video player app in Files.app will be removed in the separated patch.
BUG=271811
TEST=manually tested
R=asargent@chromium.org, hirono@chromium.org
TBR=sky@chromium.org, mkosiba@chromium.org
# TBRing for adding a resource to browser_resources.grd.
# TBRing for adding a combined script to the whitelist.
Review URL: https://codereview.chromium.org/196383030
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@259070 0039d316-1c4b-4281-b951-d872f2087c98
17 files changed, 471 insertions, 7 deletions
diff --git a/android_webview/tools/third_party_files_whitelist.txt b/android_webview/tools/third_party_files_whitelist.txt index 956ac72..a5a4139 100644 --- a/android_webview/tools/third_party_files_whitelist.txt +++ b/android_webview/tools/third_party_files_whitelist.txt @@ -45,6 +45,8 @@ chrome/browser/resources/file_manager/audio_player/js/audio_player_scripts.js # String 'copyright' used in code. chrome/browser/resources/file_manager/foreground/js/media/video_player_scripts.js # String 'copyright' used in code. +chrome/browser/resources/video_player/js/video_player_scripts.js +# String 'copyright' used in code. chrome/common/importer/firefox_importer_utils.cc # Copyright Netscape Communications Corporation; MPL, GPL v2 or LGPL v2 # license. Not used on Android. diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd index f5413f8..debf64b 100644 --- a/chrome/browser/browser_resources.grd +++ b/chrome/browser/browser_resources.grd @@ -337,6 +337,7 @@ </if> <if expr="chromeos"> <include name="IDR_FILEMANAGER_MANIFEST" file="resources\file_manager\manifest.json" type="BINDATA" /> + <include name="IDR_VIDEOPLAYER_MANIFEST" file="resources\video_player\manifest.json" type="BINDATA" /> </if> <if expr="image_loader_extension"> <include name="IDR_IMAGE_LOADER_MANIFEST" file="resources\image_loader\manifest.json" type="BINDATA" /> diff --git a/chrome/browser/extensions/component_loader.cc b/chrome/browser/extensions/component_loader.cc index 994b62e..e361d680 100644 --- a/chrome/browser/extensions/component_loader.cc +++ b/chrome/browser/extensions/component_loader.cc @@ -287,6 +287,11 @@ void ComponentLoader::AddFileManagerExtension() { #endif // defined(OS_CHROMEOS) } +void ComponentLoader::AddVideoPlayerExtension() { + Add(IDR_VIDEOPLAYER_MANIFEST, + base::FilePath(FILE_PATH_LITERAL("video_player"))); +} + void ComponentLoader::AddHangoutServicesExtension() { #if defined(GOOGLE_CHROME_BUILD) || defined(ENABLE_HANGOUT_SERVICES_EXTENSION) Add(IDR_HANGOUT_SERVICES_MANIFEST, @@ -436,6 +441,7 @@ void ComponentLoader::AddDefaultComponentExtensionsForKioskMode( // Component extensions needed for kiosk apps. AddFileManagerExtension(); + AddVideoPlayerExtension(); // Add virtual keyboard. AddKeyboardApp(); @@ -466,6 +472,7 @@ void ComponentLoader::AddDefaultComponentExtensionsWithBackgroundPages( if (!skip_session_components) { AddFileManagerExtension(); + AddVideoPlayerExtension(); AddHangoutServicesExtension(); AddHotwordHelperExtension(); AddImageLoaderExtension(); diff --git a/chrome/browser/extensions/component_loader.h b/chrome/browser/extensions/component_loader.h index b01e18a..e60c824 100644 --- a/chrome/browser/extensions/component_loader.h +++ b/chrome/browser/extensions/component_loader.h @@ -126,6 +126,7 @@ class ComponentLoader { void AddDefaultComponentExtensionsWithBackgroundPages( bool skip_session_components); void AddFileManagerExtension(); + void AddVideoPlayerExtension(); void AddHangoutServicesExtension(); void AddHotwordHelperExtension(); void AddImageLoaderExtension(); diff --git a/chrome/browser/resources/component_extension_resources.grd b/chrome/browser/resources/component_extension_resources.grd index 6257793..6b6cb3a 100644 --- a/chrome/browser/resources/component_extension_resources.grd +++ b/chrome/browser/resources/component_extension_resources.grd @@ -72,9 +72,11 @@ <include name="IDR_FILE_MANAGER_MAIN_JS" file="file_manager/foreground/js/main_scripts.js" flattenhtml="true" type="BINDATA" /> <include name="IDR_FILE_MANAGER_BKGND_JS" file="file_manager/background/js/background.js" type="BINDATA" /> - <include name="IDR_FILE_MANAGER_MEDIAPLAYER" file="file_manager/mediaplayer.html" allowexternalscript="true" flattenhtml="true" type="BINDATA" /> - <include name="IDR_FILE_MANAGER_MEDIAPLAYER_JS" file="file_manager/foreground/js/media/mediaplayer_scripts.js" flattenhtml="true" type="BINDATA" /> - + <include name="IDR_VIDEO_PLAYER" file="video_player/video_player.html" allowexternalscript="true" flattenhtml="true" type="BINDATA" /> + <include name="IDR_VIDEO_PLAYER_JS" file="video_player/js/video_player_scripts.js" flattenhtml="true" type="BINDATA" /> + <include name="IDR_VIDEO_PLAYER_BKGND_JS" file="video_player/js/background.js" flattenhtml="true" type="BINDATA" /> + <include name="IDR_VIDEO_PLAYER_ICON_16" file="video_player/images/100/icon.png" type="BINDATA" /> + <include name="IDR_VIDEO_PLAYER_ICON_32" file="video_player/images/200/icon.png" type="BINDATA" /> <include name="IDR_FILE_MANAGER_AUDIO_PLAYER" file="file_manager/audio_player.html" allowexternalscript="true" flattenhtml="true" type="BINDATA" /> <include name="IDR_FILE_MANAGER_AUDIO_PLAYER_SCRIPTS_JS" file="file_manager/audio_player/js/audio_player_scripts.js" flattenhtml="true" type="BINDATA" /> diff --git a/chrome/browser/resources/file_manager/foreground/js/media/media_controls.js b/chrome/browser/resources/file_manager/foreground/js/media/media_controls.js index 174796c..f8c48b8 100644 --- a/chrome/browser/resources/file_manager/foreground/js/media/media_controls.js +++ b/chrome/browser/resources/file_manager/foreground/js/media/media_controls.js @@ -490,8 +490,10 @@ MediaControls.prototype.encodeState = function() { if (!this.media_.duration) return; - window.appState.time = this.media_.currentTime; - util.saveAppState(); + if (window.appState) { + window.appState.time = this.media_.currentTime; + util.saveAppState(); + } return; }; @@ -500,11 +502,11 @@ MediaControls.prototype.encodeState = function() { * @return {boolean} True if decode succeeded. */ MediaControls.prototype.decodeState = function() { - if (!('time' in window.appState)) + if (!window.appState || !('time' in window.appState)) return false; // There is no page reload for apps v2, only app restart. // Always restart in paused state. - this.media_.currentTime = appState.time; + this.media_.currentTime = window.appState.time; this.pause(); return true; }; @@ -513,6 +515,9 @@ MediaControls.prototype.decodeState = function() { * Remove current state from the page URL or the app state. */ MediaControls.prototype.clearState = function() { + if (!window.appState) + return; + if ('time' in window.appState) delete window.appState.time; util.saveAppState(); diff --git a/chrome/browser/resources/video_player/OWNERS b/chrome/browser/resources/video_player/OWNERS new file mode 100644 index 0000000..f199533 --- /dev/null +++ b/chrome/browser/resources/video_player/OWNERS @@ -0,0 +1 @@ +yoshiki@chromium.org
\ No newline at end of file diff --git a/chrome/browser/resources/video_player/css/video_player.css b/chrome/browser/resources/video_player/css/video_player.css new file mode 100644 index 0000000..2c11c33 --- /dev/null +++ b/chrome/browser/resources/video_player/css/video_player.css @@ -0,0 +1,103 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +body { + -webkit-user-select: none; + background: black; + font-family: Noto Sans UI,Droid Sans Fallback,sans-serif; + font-size: 84%; + margin: 0; + overflow: hidden; +} + +#video-player { + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 100%; +} + +#video-container { + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 100%; +} + +video { + height: 100%; + left: 0; + pointer-events: none; + position: absolute; + top: 0; + width: 100%; +} + +#controls-wrapper { + -webkit-box-align: center; + -webkit-box-orient: horizontal; + -webkit-box-pack: center; + bottom: 0; + display: -webkit-box; + left: 0; + position: absolute; + right: 0; +} + +#controls { + -webkit-box-flex: 1; + display: -webkit-box; +} + +#video-player:not([tools]) .tool { + opacity: 0; +} + +#video-player:not([tools]) { + cursor: none; +} + +#video-player[disabled] .tool { + display: none; +} + +.tool { + transition: opacity 180ms linear; +} + +#error-wrapper { + -webkit-box-align: center; + -webkit-box-orient: horizontal; + -webkit-box-pack: center; + display: -webkit-box; + height: 100%; + left: 0; + pointer-events: none; + position: absolute; + top: 0; + width: 100%; +} + +#error { + -webkit-box-align: center; + -webkit-box-orient: horizontal; + -webkit-box-pack: center; + background-color: rgba(24, 24, 24, 1); + background-image: -webkit-image-set( + url('../images/100/error.png') 1x, + url('../images/200/error.png') 2x); + background-position: 25px center; + background-repeat: no-repeat; + color: white; + display: -webkit-box; + height: 54px; + padding-left: 70px; + padding-right: 35px; +} + +#error:not([visible]) { + display: none; +} diff --git a/chrome/browser/resources/video_player/images/100/error.png b/chrome/browser/resources/video_player/images/100/error.png Binary files differnew file mode 100644 index 0000000..125c0b7 --- /dev/null +++ b/chrome/browser/resources/video_player/images/100/error.png diff --git a/chrome/browser/resources/video_player/images/100/icon.png b/chrome/browser/resources/video_player/images/100/icon.png Binary files differnew file mode 100644 index 0000000..73b70f6 --- /dev/null +++ b/chrome/browser/resources/video_player/images/100/icon.png diff --git a/chrome/browser/resources/video_player/images/200/error.png b/chrome/browser/resources/video_player/images/200/error.png Binary files differnew file mode 100644 index 0000000..200baf5 --- /dev/null +++ b/chrome/browser/resources/video_player/images/200/error.png diff --git a/chrome/browser/resources/video_player/images/200/icon.png b/chrome/browser/resources/video_player/images/200/icon.png Binary files differnew file mode 100644 index 0000000..a8346a3 --- /dev/null +++ b/chrome/browser/resources/video_player/images/200/icon.png diff --git a/chrome/browser/resources/video_player/js/background.js b/chrome/browser/resources/video_player/js/background.js new file mode 100644 index 0000000..0ec6bda --- /dev/null +++ b/chrome/browser/resources/video_player/js/background.js @@ -0,0 +1,33 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +'use strict'; + +chrome.app.runtime.onLaunched.addListener(function(launchData) { + if (!launchData || !launchData.items || launchData.items.length == 0) + return; + + var entry = launchData.items[0].entry; + entry.file(function(file) { + var url = window.URL.createObjectURL(file); + open(url, entry.name); + }, function() { + // TODO(yoshiki): handle error in a smarter way. + open('', 'error'); // Empty URL shows the error message. + }); +}); + +function open(url, title) { + chrome.app.window.create('video_player.html', { + id: 'video', + singleton: false, + minWidth: 160, + minHeight: 100 + }, + function(createdWindow) { + createdWindow.setIcon('images/200/icon.png'); + createdWindow.contentWindow.videoUrl = url; + createdWindow.contentWindow.videoTitle = title; + }); +} diff --git a/chrome/browser/resources/video_player/js/video_player.js b/chrome/browser/resources/video_player/js/video_player.js new file mode 100644 index 0000000..3843a8a --- /dev/null +++ b/chrome/browser/resources/video_player/js/video_player.js @@ -0,0 +1,217 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +'use strict'; + +/** + * Displays error message. + * @param {string} message Message id. + */ +function showErrorMessage(message) { + var errorBanner = document.querySelector('#error'); + errorBanner.textContent = + loadTimeData.getString(message); + errorBanner.setAttribute('visible', 'true'); + + // The window is hidden if the video has not loaded yet. + chrome.app.window.current().show(); +} + +/** + * Handles playback (decoder) errors. + */ +function onPlaybackError() { + showErrorMessage('GALLERY_VIDEO_DECODING_ERROR'); + decodeErrorOccured = true; + + // Disable inactivity watcher, and disable the ui, by hiding tools manually. + controls.inactivityWatcher.disabled = true; + document.querySelector('#video-player').setAttribute('disabled', 'true'); + + // Detach the video element, since it may be unreliable and reset stored + // current playback time. + controls.cleanup(); + controls.clearState(); + + // Avoid reusing a video element. + video.parentNode.removeChild(video); + video = null; +} + +/** + * @param {Element} playerContainer Main container. + * @param {Element} videoContainer Container for the video element. + * @param {Element} controlsContainer Container for video controls. + * @constructor + */ +function FullWindowVideoControls( + playerContainer, videoContainer, controlsContainer) { + VideoControls.call(this, + controlsContainer, + onPlaybackError, + loadTimeData.getString.bind(loadTimeData), + this.toggleFullScreen_.bind(this), + videoContainer); + + this.playerContainer_ = playerContainer; + + this.updateStyle(); + window.addEventListener('resize', this.updateStyle.bind(this)); + + document.addEventListener('keydown', function(e) { + if (e.keyIdentifier == 'U+0020') { // Space + this.togglePlayStateWithFeedback(); + e.preventDefault(); + } + if (e.keyIdentifier == 'U+001B') { // Escape + util.toggleFullScreen( + chrome.app.window.current(), + false); // Leave the full screen mode. + e.preventDefault(); + } + }.bind(this)); + + // TODO(mtomasz): Simplify. crbug.com/254318. + videoContainer.addEventListener('click', function(e) { + if (e.ctrlKey) { + this.toggleLoopedModeWithFeedback(true); + if (!this.isPlaying()) + this.togglePlayStateWithFeedback(); + } else { + this.togglePlayStateWithFeedback(); + } + }.bind(this)); + + this.inactivityWatcher_ = new MouseInactivityWatcher(playerContainer); + this.__defineGetter__('inactivityWatcher', function() { + return this.inactivityWatcher_; + }); + + this.inactivityWatcher_.check(); + + Object.seal(this); +} + +FullWindowVideoControls.prototype = { __proto__: VideoControls.prototype }; + +/** + * Toggles the full screen mode. + * @private + */ +FullWindowVideoControls.prototype.toggleFullScreen_ = function() { + var appWindow = chrome.app.window.current(); + util.toggleFullScreen(appWindow, !util.isFullScreen(appWindow)); +}; + +// TODO(mtomasz): Convert it to class members: crbug.com/171191. +var decodeErrorOccured; +var video; +var controls; + +/** + * Initializes the video player window. + */ +function loadVideoPlayer() { + document.ondragstart = function(e) { e.preventDefault() }; + + chrome.fileBrowserPrivate.getStrings(function(strings) { + loadTimeData.data = strings; + + var url = window.videoUrl; + document.title = window.videoTitle; + + controls = new FullWindowVideoControls( + document.querySelector('#video-player'), + document.querySelector('#video-container'), + document.querySelector('#controls')); + + var reloadVideo = function(e) { + if (decodeErrorOccured && + // Ignore shortcut keys + !e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey) { + loadVideo(url); + e.preventDefault(); + } + }; + + loadVideo(url); + document.addEventListener('keydown', reloadVideo, true); + document.addEventListener('click', reloadVideo, true); + }); +} + +/** + * Unloads the player. + */ +function unload() { + if (!controls.getMedia()) + return; + + controls.savePosition(true /* exiting */); + controls.cleanup(); +} + +/** + * Reloads the player. + * @param {string} url URL of the video file. + */ +function loadVideo(url) { + // Re-enable ui and hide error message if already displayed. + document.querySelector('#video-player').removeAttribute('disabled'); + document.querySelector('#error').removeAttribute('visible'); + controls.inactivityWatcher.disabled = false; + decodeErrorOccured = false; + + // Detach the previous video element, if exists. + if (video) + video.parentNode.removeChild(video); + + video = document.createElement('video'); + document.querySelector('#video-container').appendChild(video); + controls.attachMedia(video); + + video.src = url; + video.load(); + video.addEventListener('loadedmetadata', function() { + // TODO: chrome.app.window soon will be able to resize the content area. + // Until then use approximate title bar height. + var TITLE_HEIGHT = 33; + + var aspect = video.videoWidth / video.videoHeight; + var newWidth = video.videoWidth; + var newHeight = video.videoHeight + TITLE_HEIGHT; + + var shrinkX = newWidth / window.screen.availWidth; + var shrinkY = newHeight / window.screen.availHeight; + if (shrinkX > 1 || shrinkY > 1) { + if (shrinkY > shrinkX) { + newHeight = newHeight / shrinkY; + newWidth = (newHeight - TITLE_HEIGHT) * aspect; + } else { + newWidth = newWidth / shrinkX; + newHeight = newWidth / aspect + TITLE_HEIGHT; + } + } + + var oldLeft = window.screenX; + var oldTop = window.screenY; + var oldWidth = window.outerWidth; + var oldHeight = window.outerHeight; + + if (!oldWidth && !oldHeight) { + oldLeft = window.screen.availWidth / 2; + oldTop = window.screen.availHeight / 2; + } + + var appWindow = chrome.app.window.current(); + appWindow.resizeTo(newWidth, newHeight); + appWindow.moveTo(oldLeft - (newWidth - oldWidth) / 2, + oldTop - (newHeight - oldHeight) / 2); + appWindow.show(); + + video.play(); + }); +} + +util.addPageLoadHandler(loadVideoPlayer); diff --git a/chrome/browser/resources/video_player/js/video_player_scripts.js b/chrome/browser/resources/video_player/js/video_player_scripts.js new file mode 100644 index 0000000..c1a5fbe --- /dev/null +++ b/chrome/browser/resources/video_player/js/video_player_scripts.js @@ -0,0 +1,25 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// The include directives are put into Javascript-style comments to prevent +// parsing errors in non-flattened mode. The flattener still sees them. +// Note that this makes the flattener to comment out the first line of the +// included file but that's all right since any javascript file should start +// with a copyright comment anyway. + +//<include src="../../../../../ui/webui/resources/js/cr.js"/> +//<include src="../../../../../ui/webui/resources/js/load_time_data.js"/> + +(function() { +'strict mode'; + +//<include src="../../file_manager/common/js/util.js"/> +//<include src="../../file_manager/foreground/js/media/media_controls.js"/> +//<include src="../../file_manager/foreground/js/media/util.js"/> + +//<include src="video_player.js"/> + +window.unload = unload; + +})(); diff --git a/chrome/browser/resources/video_player/manifest.json b/chrome/browser/resources/video_player/manifest.json new file mode 100644 index 0000000..fda4aab --- /dev/null +++ b/chrome/browser/resources/video_player/manifest.json @@ -0,0 +1,39 @@ +{ + // chrome-extension://jcgeabjmjgoblfofpppfkcoakmfobdko/ + "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw0/gRbJc545iEGRZs20Rl/HtrSUp8H3gJd4Y6hCe0CG1xQiJhZ5nc8qZyxa96gMxRAKBq54S6sjVVtV6uS70oU6FvrvwItByYkkqr4ZE7eMJKwMqnGItxWbh6KBodf89lpKoIy6MtYTqubBhXB/IQBZsXah90tXwRzaaJNWw+2BBRIhcPsH3ng+wgN7rwFxo4HIv9ZpqkYlx90rwkfjOmKPPnSXyXFIBJfmqfdbd8PLtcxzzOTE+vxwoXZuYWrthKm4uKfNqXIYns74sSJlqyKfctuR+nQdNh8uePv0e+/Ul3wER1/jIXULLjfyoaklyDs+ak3SDf+xWScJ+0LJ0AwIDAQAB", + "manifest_version": 2, + "name": "Video Player", + "version": "1.0", + "description": "Video Player", + "display_in_launcher": false, + "icons": { + "16": "images/100/icon.png", + "32": "images/200/icon.png" + }, + "permissions": [ + "fileSystem", + "fileBrowserPrivate", + "fullscreen", + "mediaPlayerPrivate", + "storage", + "chrome://theme/" + ], + "file_handlers": { + "video": { + "types": [ + "video/*" + ], + // TODO(yoshiki): localize this. + // TODO(yoshiki): change the text after removing the emdebed player. + "title": "Watch in VideoPlayer.app" + } + }, + "app": { + "background": { + "scripts": [ + "js/background.js" + ] + }, + "content_security_policy": "default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' chrome://theme data:; media-src 'self'; object-src 'self'" + } +} diff --git a/chrome/browser/resources/video_player/video_player.html b/chrome/browser/resources/video_player/video_player.html new file mode 100644 index 0000000..45ef353 --- /dev/null +++ b/chrome/browser/resources/video_player/video_player.html @@ -0,0 +1,28 @@ +<!-- + -- Copyright 2014 The Chromium Authors. All rights reserved. + -- Use of this source code is governed by a BSD-style license that can be + -- found in the LICENSE file. + --> +<html> +<head> + <title>#xFEFF;</title> + <link rel="icon" type="image/png" href="images/200/icon.png"> + <link rel="stylesheet" type="text/css" + href="../file_manager/foreground/css/media_controls.css"> + <link rel="stylesheet" type="text/css" href="css/video_player.css"> + + <script src="js/video_player_scripts.js"></script> +</head> +<body> + <div id="video-player" tools> + <div id="video-container"> + </div> + <div id="controls-wrapper"> + <div id="controls" class="tool"></div> + </div> + <div id="error-wrapper"> + <div id="error"></div> + </div> + </div> +</body> +</html> |