UNPKG

cordova-plugin-media-capture

Version:
248 lines (219 loc) 9.52 kB
/* * * 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. * */ var MediaFile = require('cordova-plugin-media-capture.MediaFile'); var MediaFileData = require('cordova-plugin-media-capture.MediaFileData'); var CaptureError = require('cordova-plugin-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]); // eslint-disable-line no-undef // 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 }); // eslint-disable-line no-undef } /** * 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) { var that = this; 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)); }; 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); // eslint-disable-line no-undef // 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; if (this.container) { document.body.removeChild(this.container); } }; module.exports = { captureAudio: function (successCallback, errorCallback) { if (errorCallback) { errorCallback(new CaptureError(CaptureError.CAPTURE_NOT_SUPPORTED)); } }, captureVideo: function (successCallback, errorCallback) { if (errorCallback) { errorCallback(new CaptureError(CaptureError.CAPTURE_NOT_SUPPORTED)); } }, captureImage: function (successCallback, errorCallback, args) { var fail = function (code) { if (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 () { if (successCallback) { successCallback(new MediaFileData(null, 0, img.height, img.width, 0)); } }; } }; require('cordova/exec/proxy').add('Capture', module.exports);