You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by an...@apache.org on 2015/03/16 10:42:10 UTC
cordova-plugin-media-capture git commit: CB-7963 Adds support for
browser platform
Repository: cordova-plugin-media-capture
Updated Branches:
refs/heads/master f489e29d8 -> 90d46a069
CB-7963 Adds support for browser platform
Project: http://git-wip-us.apache.org/repos/asf/cordova-plugin-media-capture/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-plugin-media-capture/commit/90d46a06
Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugin-media-capture/tree/90d46a06
Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugin-media-capture/diff/90d46a06
Branch: refs/heads/master
Commit: 90d46a06938d1538534ad48d69248bf923c1c322
Parents: f489e29
Author: Vladimir Kotikov <v-...@microsoft.com>
Authored: Mon Feb 2 18:44:38 2015 +0300
Committer: Vladimir Kotikov <v-...@microsoft.com>
Committed: Mon Mar 16 12:20:18 2015 +0300
----------------------------------------------------------------------
README.md | 11 ++
plugin.xml | 8 ++
src/browser/CaptureProxy.js | 225 +++++++++++++++++++++++++++++++++++++++
tests/tests.js | 2 +-
4 files changed, 245 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cordova-plugin-media-capture/blob/90d46a06/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
index cbf0c1e..2603a69 100644
--- a/README.md
+++ b/README.md
@@ -56,6 +56,7 @@ Although in the global scope, it is not available until after the `deviceready`
- Amazon Fire OS
- Android
- BlackBerry 10
+- Browser
- iOS
- Windows Phone 7 and 8
- Windows 8
@@ -215,6 +216,7 @@ object featuring a `CaptureError.CAPTURE_NO_MEDIA_FILES` error code.
- Amazon Fire OS
- Android
- BlackBerry 10
+- Browser
- iOS
- Windows Phone 7 and 8
- Windows 8
@@ -224,6 +226,15 @@ object featuring a `CaptureError.CAPTURE_NO_MEDIA_FILES` error code.
Invoking the native camera application while your device is connected
via Zune does not work, and the error callback executes.
+### Browser Quirks
+
+Works in Chrome, Firefox and Opera only (since IE and Safari doesn't supports
+navigator.getUserMedia API)
+
+Displaying images using captured file's URL available in Chrome/Opera only.
+Firefox stores captured images in IndexedDB storage (see File plugin documentation),
+and due to this the only way to show captured image is to read it and show using its DataURL.
+
### Example
// capture callback
http://git-wip-us.apache.org/repos/asf/cordova-plugin-media-capture/blob/90d46a06/plugin.xml
----------------------------------------------------------------------
diff --git a/plugin.xml b/plugin.xml
index ff05e58..87b3db0 100644
--- a/plugin.xml
+++ b/plugin.xml
@@ -239,4 +239,12 @@ xmlns:rim="http://www.blackberry.com/ns/widgets"
</js-module>
</platform>
+ <!-- browser -->
+ <platform name="browser">
+ <!-- this overrides navigator.device.capture namespace with browser-specific implementation -->
+ <js-module src="src/browser/CaptureProxy.js" name="CaptureProxy">
+ <runs />
+ </js-module>
+ </platform>
+
</plugin>
http://git-wip-us.apache.org/repos/asf/cordova-plugin-media-capture/blob/90d46a06/src/browser/CaptureProxy.js
----------------------------------------------------------------------
diff --git a/src/browser/CaptureProxy.js b/src/browser/CaptureProxy.js
new file mode 100644
index 0000000..0c9dd7d
--- /dev/null
+++ b/src/browser/CaptureProxy.js
@@ -0,0 +1,225 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+*/
+
+/*global require, module*/
+
+var MediaFile = require('org.apache.cordova.media-capture.MediaFile');
+var MediaFileData = require('org.apache.cordova.media-capture.MediaFileData');
+var CaptureError = require('org.apache.cordova.media-capture.CaptureError');
+
+/**
+ * Helper function that converts data URI to Blob
+ * @param {String} dataURI Data URI to convert
+ * @return {Blob} Blob, covnerted from DataURI String
+ */
+function dataURItoBlob(dataURI) {
+ // convert base64 to raw binary data held in a string
+ // doesn't handle URLEncoded DataURIs
+ var byteString = atob(dataURI.split(',')[1]);
+
+ // separate out the mime component
+ var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
+
+ // write the bytes of the string to an ArrayBuffer
+ var ab = new ArrayBuffer(byteString.length);
+ var ia = new Uint8Array(ab);
+ for (var i = 0; i < byteString.length; i++) {
+ ia[i] = byteString.charCodeAt(i);
+ }
+
+ // write the ArrayBuffer to a blob, and you're done
+ return new Blob([ab], { type: mimeString });
+}
+
+/**
+ * Creates basic camera UI with preview 'video' element and 'Cancel' button
+ * Capture starts, when you clicking on preview.
+ */
+function CameraUI() {
+
+ // Root element for preview
+ var container = document.createElement('div');
+ container.style.cssText = "left: 0px; top: 0px; width: 100%; height: 100%; position: fixed; z-index:9999;" +
+ "padding: 40px; background-color: rgba(0,0,0,0.75);" +
+ "text-align:center; visibility: hidden";
+
+ // Set up root element contetnts
+ container.innerHTML =
+ '<div id="captureHint" style="height:100%; position:relative; display:inline-flex; align-content:flex-start;">' +
+ '<h2 style="position: absolute; width: 100%; background-color: rgba(255,255,255,0.25); margin: 0">' +
+ 'Click on preview to capture image. Click outside of preview to cancel.</h1>' +
+ '<video id="capturePreview" style="height: 100%"></video>' +
+ '</div>';
+
+ // Add container element to DOM but do not display it since visibility == hidden
+ document.body.appendChild(container);
+
+ // Create fullscreen preview
+ var preview = document.getElementById('capturePreview');
+ preview.autoplay = true;
+ // We'll show preview only when video element content
+ // is fully loaded to avoid glitches
+ preview.onplay = function () {
+ container.style.visibility = 'visible';
+ };
+
+ this.container = container;
+ this.preview = preview;
+}
+
+/**
+ * Displays capture preview
+ * @param {Number} count Number of images to take
+ * @param {Function} successCB Success callback, that accepts data URL of captured image
+ * @param {Function} errorCB Error callback
+ */
+CameraUI.prototype.startPreview = function(count, successCB, errorCB) {
+
+ this.preview.onclick = function (e) {
+ // proceed with capture here
+ // We don't need to propagate click event to parent elements.
+ // Otherwise click on vieo element will trigger click event handler
+ // for preview root element and cause preview cancellation
+ e.stopPropagation();
+ // Create canvas element, put video frame on it
+ // and save its contant as Data URL
+ var canvas = document.createElement('canvas');
+ canvas.width = this.videoWidth;
+ canvas.height = this.videoHeight;
+ canvas.getContext('2d').drawImage(that.preview, 0, 0);
+ successCB(canvas.toDataURL('image/jpeg'));
+ };
+
+ this.container.onclick = function () {
+ // Cancel capture here
+ errorCB(new CaptureError(CaptureError.CAPTURE_NO_MEDIA_FILES));
+ };
+
+ var that = this;
+ navigator.getUserMedia({video: true}, function (previewStream) {
+ // Save video stream to be able to stop it later
+ that._previewStream = previewStream;
+ that.preview.src = URL.createObjectURL(previewStream);
+ // We don't need to set visibility = true for preview element
+ // since this will be done automatically in onplay event handler
+ }, function (/*err*/) {
+ errorCB(new CaptureError(CaptureError.CAPTURE_INTERNAL_ERR));
+ });
+};
+
+/**
+ * Destroys camera preview, removes all elements created
+ */
+CameraUI.prototype.destroyPreview = function () {
+ this.preview.pause();
+ this.preview.src = null;
+ this._previewStream.stop();
+ this._previewStream = null;
+ this.container && document.body.removeChild(this.container);
+};
+
+
+module.exports = {
+
+ captureAudio:function(successCallback, errorCallback) {
+ errorCallback && errorCallback(new CaptureError(CaptureError.CAPTURE_NOT_SUPPORTED));
+ },
+
+ captureVideo:function (successCallback, errorCallback) {
+ errorCallback && errorCallback(new CaptureError(CaptureError.CAPTURE_NOT_SUPPORTED));
+ },
+
+ captureImage:function (successCallback, errorCallback, args) {
+
+ var fail = function (code) {
+ errorCallback && errorCallback(new CaptureError(code || CaptureError.CAPTURE_INTERNAL_ERR));
+ };
+
+ var options = args[0];
+
+ var limit = options.limit || 1;
+ if (typeof limit !== 'number' || limit < 1) {
+ fail(CaptureError.CAPTURE_INVALID_ARGUMENT);
+ return;
+ }
+
+ // Counter for already taken images
+ var imagesTaken = 0;
+
+ navigator.getUserMedia = navigator.getUserMedia ||
+ navigator.webkitGetUserMedia ||
+ navigator.mozGetUserMedia ||
+ navigator.msGetUserMedia;
+
+ if (!navigator.getUserMedia) {
+ fail(CaptureError.CAPTURE_NOT_SUPPORTED);
+ return;
+ }
+
+ var ui = new CameraUI();
+ ui.startPreview(limit, function (data) {
+ // Check if we're done with capture. If so, then destroy UI
+ if (++imagesTaken >= limit) {
+ ui.destroyPreview();
+ }
+
+ // Array of resultant MediaFiles
+ var mediaFiles = [];
+
+ // save data to file here
+ window.requestFileSystem(window.TEMPORARY, data.length * limit, function (fileSystem) {
+ // If we need to capture multiple files, then append counter to filename
+ var fileName = limit <= 1 ? 'image.jpg' : 'image' + imagesTaken + '.jpg';
+ fileSystem.root.getFile(fileName, {create: true}, function (file) {
+ file.createWriter(function (writer) {
+ writer.onwriteend = function () {
+ file.getMetadata(function (meta) {
+ mediaFiles.push(new MediaFile(file.name, file.toURL(), 'image/jpeg', meta.modificationTime, meta.size));
+ // Check if we're done with capture. If so, then call a successCallback
+ if (imagesTaken >= limit) {
+ successCallback(mediaFiles);
+ }
+ }, fail);
+ };
+ writer.onerror = fail;
+ // Since success callback for start preview returns
+ // a base64 encoded string, we need to convert it to blob first
+ writer.write(dataURItoBlob(data));
+ });
+ }, fail);
+ }, fail);
+ }, function (err) {
+ ui.destroyPreview();
+ fail(err.code);
+ });
+ },
+
+ getFormatData: function (successCallback, errorCallback, args) {
+
+ var img = document.createElement('img');
+ img.src = args[0];
+ img.onload = function () {
+ successCallback && successCallback(new MediaFileData(null, 0, img.height, img.width, 0));
+ };
+ }
+};
+
+require("cordova/exec/proxy").add("Capture",module.exports);
http://git-wip-us.apache.org/repos/asf/cordova-plugin-media-capture/blob/90d46a06/tests/tests.js
----------------------------------------------------------------------
diff --git a/tests/tests.js b/tests/tests.js
index c98f64d..28c3dd2 100644
--- a/tests/tests.js
+++ b/tests/tests.js
@@ -146,7 +146,7 @@ exports.defineManualTests = function (contentEl, createActionButton) {
function captureImageWin(mediaFiles) {
var path = mediaFiles[0].fullPath;
// Necessary since windows doesn't allow file URLs for <img> elements
- if (cordova.platformId == 'windows' || cordova.platformId == 'windows8') {
+ if (cordova.platformId == 'windows' || cordova.platformId == 'windows8' || cordova.platformId === 'browser') {
path = mediaFiles[0].localURL;
}
log('Image captured: ' + path);
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org