Skip to content
This repository has been archived by the owner on Nov 3, 2021. It is now read-only.

Commit

Permalink
Merge pull request #25908 from davidflanagan/bug1079519-2
Browse files Browse the repository at this point in the history
Bug 1079519 - Unable to play video clips during WEBRTC video call r=russn,djf
  • Loading branch information
David Flanagan committed Nov 7, 2014
2 parents 4684209 + ddeca09 commit 256dc07
Show file tree
Hide file tree
Showing 9 changed files with 195 additions and 29 deletions.
8 changes: 8 additions & 0 deletions apps/video/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
<script defer src="js/thumbnail_item.js"></script>
<script defer src="js/video_utils.js"></script>
<script defer src="js/forward_rewind_controller.js"></script>
<script defer src="js/video_loading_checker.js"></script>
<script defer src="js/video.js"></script>
</head>
<body role="application" class="skin-dark theme-media">
Expand Down Expand Up @@ -197,5 +198,12 @@ <h1 id="overlay-title"></h1>
<button id="confirm-ok"></button>
</menu>
</form>
<!-- display "Cannot load video because it is in use" here -->
<form id="in-use-overlay" role="dialog" data-type="confirm" class="hidden">
<section id="in-use-overlay-content">
<h1 id="in-use-overlay-title"></h1>
<p id="in-use-overlay-text"></p>
</section>
</form>
</body>
</html>
24 changes: 18 additions & 6 deletions apps/video/js/video.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ var ids = ['thumbnail-list-view', 'thumbnails-bottom', 'thumbnail-list-title',
'video-title', 'duration-text', 'elapsed-text', 'bufferedTime',
'slider-wrapper', 'throbber', 'picker-close', 'picker-title',
'picker-header', 'picker-done', 'options', 'options-view',
'options-cancel-button', 'seek-backward', 'seek-forward'];
'options-cancel-button', 'seek-backward', 'seek-forward',
'in-use-overlay', 'in-use-overlay-title', 'in-use-overlay-text'];

ids.forEach(function createElementRef(name) {
dom[toCamelCase(name)] = document.getElementById(name);
Expand Down Expand Up @@ -98,6 +99,12 @@ var FROMCAMERA = /DCIM\/\d{3}MZLLA\/VID_\d{4}\.3gp$/;

var videoControlsAutoHidingMsOverride;

// We have a single instance of the loading checker because it is used
// across functions
var loadingChecker =
new VideoLoadingChecker(dom.player, dom.inUseOverlay, dom.inUseOverlayTitle,
dom.inUseOverlayText);

// Pause on visibility change
document.addEventListener('visibilitychange', function visibilityChange() {
if (document.hidden) {
Expand Down Expand Up @@ -916,18 +923,21 @@ function setVideoUrl(player, video, callback) {
callback();
}

function loadVideo(url) {
loadingChecker.ensureVideoLoads(handleLoadedMetadata);
player.src = url;
}

if ('name' in video) {
videodb.getFile(video.name, function(file) {
var url = URL.createObjectURL(file);
player.onloadedmetadata = handleLoadedMetadata;
player.src = url;
loadVideo(url);

if (pendingPick)
currentVideoBlob = file;
});
} else if ('url' in video) {
player.onloadedmetadata = handleLoadedMetadata;
player.src = video.url;
loadVideo(video.url);
}
}

Expand Down Expand Up @@ -1136,6 +1146,7 @@ function playerEnded() {
}

function play() {
loadingChecker.ensureVideoPlays();
// Switch the button icon
dom.play.classList.remove('paused');

Expand All @@ -1146,12 +1157,13 @@ function play() {
// by setting the media.mediasource.enabled pref to true.
VideoStats.start(dom.player);

// Start playing
dom.player.play();
playing = true;
}

function pause() {
loadingChecker.cancelEnsureVideoPlays();

// Switch the button icon
dom.play.classList.add('paused');

Expand Down
83 changes: 83 additions & 0 deletions apps/video/js/video_loading_checker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Bug 1079519: If a WebRTC call (from the Hello app) is running in the
* background, then the video decoding hardware may be in use, and the video
* app will not be able to play h264 videos that require hardware decoding.
* The <video> element does not have any way to report this condition currently
* and instead just sits and waits its turn to use the hardware.
*
* This modules is a hacky workaround: if we do not get loadedmetadata
* or timeupdate events quickly enough we assume that the video
* hardware is in use and display an error message to the user.
*
* Bug 1093283 has been filed for a real fix to the video element and
* if we get that fix, then we can check for this 'hardware in use'
* condition explicitly and can remove this hack.
*/
(function(exports) {
'use strict';

exports.VideoLoadingChecker = VideoLoadingChecker;

function VideoLoadingChecker(player, overlay, overlayTitle, overlayText) {
this.player = player;
this.overlay = overlay;
this.overlayTitle = overlayTitle;
this.overlayText = overlayText;
}

VideoLoadingChecker.prototype = {

ensureVideoLoads: function vpc_ensureVideoLoads(callback) {
var self = this;
var videoLoadedTimeoutId = setTimeout(showOverlay.bind(null, this), 1000);
this.player.addEventListener('loadedmetadata', handleLoadedMetadata);

function handleLoadedMetadata() {

// Video was loaded, don't display the 'video could not be loaded'
// overlay (or hide it).
clearTimeout(videoLoadedTimeoutId);
videoLoadedTimeoutId = null;
self.player.removeEventListener('loadedmetadata', handleLoadedMetadata);
hideOverlay(self);
if (callback) {
callback();
}
}
},

ensureVideoPlays: function() {
this.playingTimeout = setTimeout(showOverlay.bind(null, this), 1000);
this.playingListener = this.cancelEnsureVideoPlays.bind(this);
// When the video hardware is in use, we still get a playing event
// after calling play(). So to ensure that playback is actually
// happening, we listen for a timeupdate event instead.
this.player.addEventListener('timeupdate', this.playingListener);
},

cancelEnsureVideoPlays: function() {
hideOverlay(this);
if (this.playingTimeout) {
clearTimeout(this.playingTimeout);
this.playingTimeout = null;
}
if (this.playingListener) {
this.player.removeEventListener('timeupdate', this.playingListener);
this.playingListener = null;
}
}
};

function showOverlay(checker) {
checker.overlayTitle.setAttribute('data-l10n-id',
'video-hardware-in-use-title');
checker.overlayText.setAttribute('data-l10n-id',
'video-hardware-in-use-text');
checker.overlay.classList.remove('hidden');
}

function hideOverlay(checker) {
checker.overlay.classList.add('hidden');
}

}(window));
23 changes: 16 additions & 7 deletions apps/video/js/view.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@ navigator.mozSetMessageHandler('activity', function viewVideo(activity) {
'elapsedTime', 'video-title', 'duration-text', 'elapsed-text',
'slider-wrapper', 'spinner-overlay',
'save', 'banner', 'message', 'seek-forward',
'seek-backward', 'videoControlBar'];
'seek-backward', 'videoControlBar', 'in-use-overlay',
'in-use-overlay-title', 'in-use-overlay-text'];

ids.forEach(function createElementRef(name) {
dom[toCamelCase(name)] = document.getElementById(name);
Expand Down Expand Up @@ -281,10 +282,7 @@ navigator.mozSetMessageHandler('activity', function viewVideo(activity) {

// show video player
function showPlayer(url, title) {

dom.videoTitle.textContent = title || '';
dom.player.src = url;
dom.player.onloadedmetadata = function() {
function handleLoadedMetadata() {
dom.durationText.textContent = MediaUtils.formatDuration(
dom.player.duration);
timeUpdated();
Expand All @@ -296,13 +294,24 @@ navigator.mozSetMessageHandler('activity', function viewVideo(activity) {
dom.player.currentTime = 0;

// Show the controls briefly then fade out
setControlsVisibility(true);
controlFadeTimeout = setTimeout(function() {
setControlsVisibility(false);
}, 2000);

play();
};
}

dom.videoTitle.textContent = title || '';
setControlsVisibility(true);

var loadingChecker =
new VideoLoadingChecker(dom.player, dom.inUseOverlay,
dom.inUseOverlayTitle,
dom.inUseOverlayText);
loadingChecker.ensureVideoLoads(handleLoadedMetadata);

dom.player.src = url;

dom.player.onloadeddata = function(evt) { URL.revokeObjectURL(url); };
dom.player.onerror = function(evt) {
var errorid = '';
Expand Down
4 changes: 4 additions & 0 deletions apps/video/locales/video.en-US.properties
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,12 @@ saved = Saved {{title}} to Video

error-network = The video could not be downloaded because of a network error.
error-unsupported = Video cannot be played: either because the server or network failed or because the format is not supported.

error-unknown = An unknown video error occurred.

video-hardware-in-use-title = Cannot play video
video-hardware-in-use-text = Another app is currently using the video player.

overlay-cancel-button = Cancel
overlay-camera-button = Go to Camera

Expand Down
19 changes: 16 additions & 3 deletions apps/video/style/video.css
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ span.line-break {
* is displayed.
*/
#overlay,
#in-use-overlay,
#confirm-dialog {
/* it takes up the whole screen */
position: absolute;
Expand All @@ -248,12 +249,24 @@ span.line-break {
background-color: rgba(0, 0, 0, 0.4);
}

/*
* In view.html (but not index.html) we put the in-use-overlay element
* inside videoControls. In that case there is a 5rem title bar that needs
* to be visible, so we alter the top position. And if we're handling
* a pick activity, we also need the title bar with the back button to show.
*/
#videoControls > #in-use-overlay,
body.pick-activity > #in-use-overlay {
top: 5rem;
}


/*
* The overlay content area holds the text of the overlay.
* It has borders and a less transparent background so that
* the overlay text shows up more clearly
*/
#overlay-content {
#overlay-content, #in-use-overlay-content {
background:
url(images/ui/pattern.png) repeat left top,
url(images/ui/gradient.png) no-repeat left top;
Expand All @@ -274,15 +287,15 @@ span.line-break {
padding: 11rem 2.5rem 0 2.5rem;
}

#overlay-title {
#overlay-title, #in-use-overlay-title {
font-weight: normal;
font-size: 1.9rem;
color: #fff;
margin: 0 0.5rem 0 0.5rem;
padding: 0;
}

#overlay-text {
#overlay-text, #in-use-overlay-text {
padding: 1rem 0.5rem 0 0.5rem;
border-top: 0.1rem solid #686868;
font-weight: 300;
Expand Down
27 changes: 27 additions & 0 deletions apps/video/test/unit/mock_video_loading_checker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'use strict';

function VideoLoadingChecker(player, overlay, overlayTitle, overlayText) {
this.player = player;
this.overlay = overlay;
this.overlayTitle = overlayTitle;
this.overlayText = overlayText;
this.callback = null;
}

VideoLoadingChecker.prototype = {

ensureVideoLoads: function vpc_ensureVideoLoads(callback) {
this.callback = callback;
},
invokeLoadedMetadataCallback: function vpc_invokeLoadedMetadataCallback() {
if (this.callback) {
this.callback();
}
},
resetLoadedMetadataCallback: function vpc_resetLoadedMetadataCallback() {
this.callback = null;
},
ensureVideoPlays: function() {},
cancelEnsureVideoPlays: function() {},
};

Loading

0 comments on commit 256dc07

Please sign in to comment.