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