Home | History | Annotate | Download | only in media
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 'use strict';
      6 
      7 /**
      8  * Display error message.
      9  * @param {string} message Message id.
     10  */
     11 function showErrorMessage(message) {
     12   var errorBanner = document.querySelector('#error');
     13   errorBanner.textContent =
     14       loadTimeData.getString(message);
     15   errorBanner.setAttribute('visible', 'true');
     16 
     17   // The window is hidden if the video has not loaded yet.
     18   chrome.app.window.current().show();
     19 }
     20 
     21 /**
     22  * Handles playback (decoder) errors.
     23  */
     24 function onPlaybackError() {
     25   showErrorMessage('GALLERY_VIDEO_DECODING_ERROR');
     26   decodeErrorOccured = true;
     27 
     28   // Disable inactivity watcher, and disable the ui, by hiding tools manually.
     29   controls.inactivityWatcher.disabled = true;
     30   document.querySelector('#video-player').setAttribute('disabled', 'true');
     31 
     32   // Detach the video element, since it may be unreliable and reset stored
     33   // current playback time.
     34   controls.cleanup();
     35   controls.clearState();
     36 
     37   // Avoid reusing a video element.
     38   video.parentNode.removeChild(video);
     39   video = null;
     40 }
     41 
     42 /**
     43  * @param {Element} playerContainer Main container.
     44  * @param {Element} videoContainer Container for the video element.
     45  * @param {Element} controlsContainer Container for video controls.
     46  * @constructor
     47  */
     48 function FullWindowVideoControls(
     49     playerContainer, videoContainer, controlsContainer) {
     50   VideoControls.call(this,
     51       controlsContainer,
     52       onPlaybackError,
     53       loadTimeData.getString.bind(loadTimeData),
     54       this.toggleFullScreen_.bind(this),
     55       videoContainer);
     56 
     57   this.playerContainer_ = playerContainer;
     58 
     59   this.updateStyle();
     60   window.addEventListener('resize', this.updateStyle.bind(this));
     61 
     62   document.addEventListener('keydown', function(e) {
     63     if (e.keyIdentifier == 'U+0020') {  // Space
     64       this.togglePlayStateWithFeedback();
     65       e.preventDefault();
     66     }
     67     if (e.keyIdentifier == 'U+001B') {  // Escape
     68       util.toggleFullScreen(
     69           chrome.app.window.current(),
     70           false);  // Leave the full screen mode.
     71       e.preventDefault();
     72     }
     73   }.bind(this));
     74 
     75   // TODO(mtomasz): Simplify. crbug.com/254318.
     76   videoContainer.addEventListener('click', function(e) {
     77     if (e.ctrlKey) {
     78       this.toggleLoopedModeWithFeedback(true);
     79       if (!this.isPlaying())
     80         this.togglePlayStateWithFeedback();
     81     } else {
     82       this.togglePlayStateWithFeedback();
     83     }
     84   }.bind(this));
     85 
     86   this.inactivityWatcher_ = new MouseInactivityWatcher(playerContainer);
     87   this.__defineGetter__('inactivityWatcher', function() {
     88     return this.inactivityWatcher_;
     89   });
     90 
     91   this.inactivityWatcher_.check();
     92 }
     93 
     94 FullWindowVideoControls.prototype = { __proto__: VideoControls.prototype };
     95 
     96 /**
     97  * Save the current state so that it survives page/app reload.
     98  */
     99 FullWindowVideoControls.prototype.onPlayStateChanged = function() {
    100   this.encodeState();
    101 };
    102 
    103 /**
    104  * Restore the state after the video is loaded.
    105  */
    106 FullWindowVideoControls.prototype.restorePlayState = function() {
    107   if (!this.decodeState()) {
    108     VideoControls.prototype.restorePlayState.apply(this, arguments);
    109     this.play();
    110   }
    111 };
    112 
    113 /**
    114  * Toggles the full screen mode.
    115  * @private
    116  */
    117 FullWindowVideoControls.prototype.toggleFullScreen_ = function() {
    118   var appWindow = chrome.app.window.current();
    119   util.toggleFullScreen(appWindow, !util.isFullScreen(appWindow));
    120 };
    121 
    122 // TODO(mtomasz): Convert it to class members: crbug.com/171191.
    123 var decodeErrorOccured;
    124 var video;
    125 var controls;
    126 var metadataCache;
    127 var volumeManager;
    128 var selectedItemFilesystemPath;
    129 
    130 /**
    131  * Initialize the video player window.
    132  */
    133 function loadVideoPlayer() {
    134   document.ondragstart = function(e) { e.preventDefault() };
    135 
    136   chrome.fileBrowserPrivate.getStrings(function(strings) {
    137     loadTimeData.data = strings;
    138 
    139     controls = new FullWindowVideoControls(
    140        document.querySelector('#video-player'),
    141        document.querySelector('#video-container'),
    142        document.querySelector('#controls'));
    143 
    144     metadataCache = MetadataCache.createFull();
    145     volumeManager = VolumeManager.getInstance();
    146 
    147     // If the video player is starting before the first instance of the File
    148     // Manager then it does not have access to filesystem URLs. Request it now.
    149     chrome.fileBrowserPrivate.requestFileSystem(reload);
    150 
    151     volumeManager.addEventListener('externally-unmounted',
    152                                    onExternallyUnmounted);
    153 
    154     var reloadVideo = function(e) {
    155       if (decodeErrorOccured) {
    156         reload();
    157         e.preventDefault();
    158       }
    159     };
    160 
    161     document.addEventListener('keydown', reloadVideo, true);
    162     document.addEventListener('click', reloadVideo, true);
    163 
    164   });
    165 }
    166 
    167 /**
    168  * Closes video player when a volume containing the played item is unmounted.
    169  * @param {Event} event The unmount event.
    170  */
    171 function onExternallyUnmounted(event) {
    172   if (!selectedItemFilesystemPath)
    173     return;
    174   if (selectedItemFilesystemPath.indexOf(event.mountPath) == 0)
    175     window.close();
    176 }
    177 
    178 /**
    179  * Unload the player.
    180  */
    181 function unload() {
    182   if (!controls.getMedia())
    183     return;
    184 
    185   controls.savePosition(true /* exiting */);
    186   controls.cleanup();
    187 }
    188 
    189 /**
    190  * Reload the player.
    191  */
    192 function reload() {
    193   // Re-enable ui and hide error message if already displayed.
    194   document.querySelector('#video-player').removeAttribute('disabled');
    195   document.querySelector('#error').removeAttribute('visible');
    196   controls.inactivityWatcher.disabled = false;
    197   decodeErrorOccured = false;
    198 
    199   var src;
    200   if (window.appState) {
    201     util.saveAppState();
    202     src = window.appState.url;
    203   } else {
    204     src = document.location.search.substr(1);
    205   }
    206   if (!src) {
    207     showErrorMessage('GALLERY_VIDEO_ERROR');
    208     return;
    209   }
    210 
    211   document.title = decodeURIComponent(src.split('/').pop());
    212 
    213   metadataCache.get(src, 'streaming', function(streaming) {
    214     if (streaming && !navigator.onLine) {
    215       showErrorMessage('GALLERY_VIDEO_OFFLINE');
    216       return;
    217     }
    218 
    219     // Detach the previous video element, if exists.
    220     if (video) {
    221       video.parentNode.removeChild(video);
    222     }
    223 
    224     video = document.createElement('video');
    225     document.querySelector('#video-container').appendChild(video);
    226     controls.attachMedia(video);
    227 
    228     video.src = src;
    229     video.load();
    230     video.addEventListener('loadedmetadata', function() {
    231       // TODO: chrome.app.window soon will be able to resize the content area.
    232       // Until then use approximate title bar height.
    233       var TITLE_HEIGHT = 28;
    234 
    235       var aspect = video.videoWidth / video.videoHeight;
    236       var newWidth = video.videoWidth;
    237       var newHeight = video.videoHeight + TITLE_HEIGHT;
    238 
    239       var shrinkX = newWidth / window.screen.availWidth;
    240       var shrinkY = newHeight / window.screen.availHeight;
    241       if (shrinkX > 1 || shrinkY > 1) {
    242         if (shrinkY > shrinkX) {
    243           newHeight = newHeight / shrinkY;
    244           newWidth = (newHeight - TITLE_HEIGHT) * aspect;
    245         } else {
    246           newWidth = newWidth / shrinkX;
    247           newHeight = newWidth / aspect + TITLE_HEIGHT;
    248         }
    249       }
    250 
    251       var oldLeft = window.screenX;
    252       var oldTop = window.screenY;
    253       var oldWidth = window.outerWidth;
    254       var oldHeight = window.outerHeight;
    255 
    256       if (!oldWidth && !oldHeight) {
    257         oldLeft = window.screen.availWidth / 2;
    258         oldTop = window.screen.availHeight / 2;
    259       }
    260 
    261       var appWindow = chrome.app.window.current();
    262       appWindow.resizeTo(newWidth, newHeight);
    263       appWindow.moveTo(oldLeft - (newWidth - oldWidth) / 2,
    264                        oldTop - (newHeight - oldHeight) / 2);
    265       appWindow.show();
    266     });
    267 
    268     // Resolve real filesystem path of the current video.
    269     selectedItemFilesystemPath = null;
    270     webkitResolveLocalFileSystemURL(src,
    271       function(entry) {
    272         var video = document.querySelector('video');
    273         if (video.src != src) return;
    274         selectedItemFilesystemPath = entry.fullPath;
    275       });
    276   });
    277 }
    278 
    279 util.addPageLoadHandler(loadVideoPlayer);
    280