diff --git a/src/plugins/Webcam.js b/src/plugins/Webcam.js
deleted file mode 100644
index 414876a6a..000000000
--- a/src/plugins/Webcam.js
+++ /dev/null
@@ -1,353 +0,0 @@
-import Plugin from './Plugin'
-import html from '../core/html'
-import {dataURItoFile} from '../core/Utils'
-
-/**
- * Webcam
- */
-export default class Webcam extends Plugin {
- constructor (core, opts) {
- super(core, opts)
- this.type = 'acquirer'
- this.id = 'Webcam'
- this.title = 'Webcam'
- this.icon = html`
-
- `
-
- this.snapshotIcon = html`
-
- `
-
- // set default options
- const defaultOptions = {}
-
- // merge default options with the ones set by user
- this.opts = Object.assign({}, defaultOptions, opts)
-
- this.install = this.install.bind(this)
- this.updateState = this.updateState.bind(this)
-
- this.render = this.render.bind(this)
-
- // Camera controls
- this.startWebcam = this.startWebcam.bind(this)
- this.stopWebcam = this.stopWebcam.bind(this)
- this.startRecording = this.startRecording.bind(this)
- this.stopRecording = this.stopRecording.bind(this)
- this.takeSnapshot = this.takeSnapshot.bind(this)
- this.generateImage = this.generateImage.bind(this)
-
- // Stream getting callbacks
- this.onGotStream = this.onGotStream.bind(this)
- this.onNoStream = this.onNoStream.bind(this)
- }
-
- /**
- * Checks browser support for getting user media,
- * then initializes a new media capture.
- */
- startWebcam () {
- this.video = document.querySelector('.UppyWebcam-video')
- this.canvas = document.querySelector('.UppyWebcam-canvas')
-
- const { onGotStream, onNoStream } = this
-
- if ((typeof window === 'undefined') || (typeof navigator === 'undefined')) {
- console.log('This page needs a Web browser with the objects window.* and navigator.*!')
- } else if (!(this.video && this.canvas)) {
- console.log('HTML context error!')
- } else {
- console.log('Get user media…')
- if (navigator.getUserMedia) navigator.getUserMedia({ video: true }, onGotStream, onNoStream)
- else if (navigator.oGetUserMedia) navigator.oGetUserMedia({ video: true }, onGotStream, onNoStream)
- else if (navigator.mozGetUserMedia) navigator.mozGetUserMedia({ video: true }, onGotStream, onNoStream)
- else if (navigator.webkitGetUserMedia) navigator.webkitGetUserMedia({ video: true }, onGotStream, onNoStream)
- else if (navigator.msGetUserMedia) navigator.msGetUserMedia({ video: true, audio: false }, onGotStream, onNoStream)
- else console.log('getUserMedia() not available from your Web browser!')
- }
- }
-
- /**
- * Stops the webcam capture and video playback.
- */
- stopWebcam () {
- let { video, videoStream } = this
-
- this.updateState({
- cameraReady: false
- })
-
- if (videoStream) {
- if (videoStream.stop) {
- videoStream.stop()
- } else if (videoStream.msStop) {
- videoStream.msStop()
- }
-
- videoStream.onended = null
- videoStream = null
- }
-
- if (video) {
- video.onerror = null
- video.pause()
-
- if (video.mozSrcObject) {
- video.mozSrcObject = null
- }
-
- video.src = ''
- }
-
- this.video = document.querySelector('.UppyWebcam-video')
- this.canvas = document.querySelector('.UppyWebcam-canvas')
- }
-
- /**
- * When `startWebcam` successfully captures media,
- * this callback sets up video playback in the DOM.
- *
- * @param {MediaStream} stream user media stream
- */
- onGotStream (stream) {
- console.log('Got stream!')
- this.updateState({
- cameraReady: true
- })
- this.videoStream = stream
- this.mediaRecorder = new window.MediaRecorder(stream)
-
- const video = this.video
-
- console.log('Got stream.')
-
- video.onerror = () => {
- console.log('video.onerror')
- if (video) {
- this.stopWebcam()
- }
- }
-
- stream.onended = this.onNoStream
-
- if (window.webkitURL) {
- video.src = window.webkitURL.createObjectURL(stream)
- } else if (video.mozSrcObject !== undefined) {
- video.mozSrcObject = stream
- video.play()
- } else if (navigator.mozGetUserMedia) {
- video.src = stream
- video.play()
- } else if (window.URL) {
- video.src = window.URL.createObjectURL(stream)
- } else {
- video.src = stream
- }
-
- this.video = document.querySelector('.UppyWebcam-video')
- this.canvas = document.querySelector('.UppyWebcam-canvas')
- }
-
- /**
- * Error callback when capturing user media fails.
- */
- onNoStream () {
- console.log('Access to camera was denied!')
- }
-
- /**
- * Begins recording the webcam stream and handles the media
- * after recording ends.
- */
- startRecording () {
- if (!this.videoStream) {
- console.log('Error: no video stream available')
- return
- }
-
- if (!this.mediaRecorder) {
- this.mediaRecorder = new window.MediaRecorder(this.videoStream)
- }
-
- let chunks = []
-
- this.mediaRecorder.onstop = (e) => {
- var blob = new Blob(chunks, {type: 'video/webm'})
- chunks = []
- const clip = window.URL.createObjectURL(blob)
- this.video.src = clip
- }
-
- this.mediaRecorder.ondataavailable = (e) => {
- chunks.push(e.data)
- }
-
- this.mediaRecorder.start()
-
- this.updateState({
- recording: true
- })
-
- console.log(this.mediaRecorder.state)
- console.log('recorder started')
- }
-
- /**
- * Ends media recording
- */
- stopRecording () {
- if (!this.mediaRecorder) {
- console.log('no media recorder exists')
- return
- }
-
- this.updateState({
- recording: false
- })
-
- this.mediaRecorder.stop()
- }
-
- /**
- * Takes a snapshot and displays it in a canvas.
- */
- generateImage (video, canvas, opts) {
- canvas.width = video.videoWidth
- canvas.height = video.videoHeight
- canvas.getContext('2d').drawImage(video, 0, 0)
-
- var dataUrl = canvas.toDataURL(opts.mimeType)
-
- var file = dataURItoFile(dataUrl, {
- name: opts.name
- })
-
- return {
- dataUrl: dataUrl,
- data: file,
- type: opts.mimeType
- }
- }
-
- takeSnapshot () {
- const {video, canvas, generateImage} = this
- const opts = {
- name: `webcam-${Date.now()}.jpg`,
- mimeType: 'image/jpeg'
- }
-
- var image = generateImage(video, canvas, opts)
-
- const tagFile = {
- source: this.id,
- name: opts.name,
- data: image.data,
- type: opts.type
- }
-
- this.core.emitter.emit('file-add', tagFile)
- }
-
- render (state) {
- console.log('RENDER METHOD')
- // if (!state.webcam.cameraReady) {
- // return this.renderMenu(state.webcam)
- // }
-
- return this.renderPlayer(state.webcam)
- }
-
- renderMenu (state) {
- return html`
-
-
+ ${video}
diff --git a/src/plugins/Webcam/WebcamIcon.js b/src/plugins/Webcam/WebcamIcon.js
new file mode 100644
index 000000000..4cd9dacf8
--- /dev/null
+++ b/src/plugins/Webcam/WebcamIcon.js
@@ -0,0 +1,12 @@
+import html from '../../core/html'
+
+export default (props) => {
+ return html`
+
+ `
+}
diff --git a/src/plugins/Webcam/index.js b/src/plugins/Webcam/index.js
index 606dd89ab..a5ba45b0d 100644
--- a/src/plugins/Webcam/index.js
+++ b/src/plugins/Webcam/index.js
@@ -1,8 +1,10 @@
-import Plugin from './Plugin'
-import html from '../core/html'
-import {dataURItoFile} from '../core/Utils'
+import Plugin from '../Plugin'
+import {extend, dataURItoFile} from '../../core/Utils'
import CameraScreen from './CameraScreen'
import PermissionsScreen from './PermissionsScreen'
+import WebcamIcon from './WebcamIcon'
+import html from '../../core/html'
+var _userMedia
/**
* Webcam
@@ -10,27 +12,37 @@ import PermissionsScreen from './PermissionsScreen'
export default class Webcam extends Plugin {
constructor (core, opts) {
super(core, opts)
+ this.userMedia = true
+ this.protocol = location.protocol.match(/https/i) ? 'https' : 'http'
+ this.init()
this.type = 'acquirer'
this.id = 'Webcam'
this.title = 'Webcam'
- this.icon = html`
-
- `
-
- this.snapshotIcon = html`
-
- `
+ this.icon = WebcamIcon()
// set default options
- const defaultOptions = {}
+ const defaultOptions = {
+ enableFlash: true
+ }
+
+ this.params = {
+ swfURL: 'webcam.swf',
+ width: 400,
+ height: 300,
+ dest_width: 800, // size of captured image
+ dest_height: 600, // these default to width/height
+ image_format: 'jpeg', // image format (may be jpeg or png)
+ jpeg_quality: 90, // jpeg image quality from 0 (worst) to 100 (best)
+ enable_flash: true, // enable flash fallback,
+ force_flash: false, // force flash mode,
+ flip_horiz: false, // flip image horiz (mirror mode)
+ fps: 30, // camera frames per second
+ upload_name: 'webcam', // name of file in upload post data
+ constraints: null, // custom user media constraints,
+ flashNotDetectedText: 'ERROR: No Adobe Flash Player detected. Webcam.js relies on Flash for browsers that do not support getUserMedia (like yours).',
+ noInterfaceFoundText: 'No supported webcam interface found.',
+ unfreeze_snap: true // Whether to unfreeze the camera after snap (defaults to true)
+ }
// merge default options with the ones set by user
this.opts = Object.assign({}, defaultOptions, opts)
@@ -41,41 +53,203 @@ export default class Webcam extends Plugin {
this.render = this.render.bind(this)
// Camera controls
- this.startWebcam = this.startWebcam.bind(this)
+ this.start = this.start.bind(this)
+ this.init = this.init.bind(this)
this.stopWebcam = this.stopWebcam.bind(this)
this.startRecording = this.startRecording.bind(this)
this.stopRecording = this.stopRecording.bind(this)
this.takeSnapshot = this.takeSnapshot.bind(this)
this.generateImage = this.generateImage.bind(this)
-
- // Stream getting callbacks
- this.onGotStream = this.onGotStream.bind(this)
- this.onNoStream = this.onNoStream.bind(this)
+ this.getSWFHTML = this.getSWFHTML.bind(this)
+ this.detectFlash = this.detectFlash.bind(this)
}
/**
- * Checks browser support for getting user media,
- * then initializes a new media capture.
+ * Checks for getUserMedia support
*/
- startWebcam () {
- this.video = document.querySelector('.UppyWebcam-video')
- this.canvas = document.querySelector('.UppyWebcam-canvas')
+ init () {
+ // initialize, check for getUserMedia support
- const { onGotStream, onNoStream } = this
+ // Setup getUserMedia, with polyfill for older browsers
+ // Adapted from: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
+ this.mediaDevices = (navigator.mediaDevices && navigator.mediaDevices.getUserMedia)
+ ? navigator.mediaDevices : ((navigator.mozGetUserMedia || navigator.webkitGetUserMedia) ? {
+ getUserMedia: function (c) {
+ return new Promise(function (resolve, reject) {
+ (navigator.mozGetUserMedia ||
+ navigator.webkitGetUserMedia).call(navigator, c, resolve, reject)
+ })
+ }
+ } : null)
- if ((typeof window === 'undefined') || (typeof navigator === 'undefined')) {
- console.log('This page needs a Web browser with the objects window.* and navigator.*!')
- } else if (!(this.video && this.canvas)) {
- console.log('HTML context error!')
- } else {
- console.log('Get user media…')
- if (navigator.getUserMedia) navigator.getUserMedia({ video: true }, onGotStream, onNoStream)
- else if (navigator.oGetUserMedia) navigator.oGetUserMedia({ video: true }, onGotStream, onNoStream)
- else if (navigator.mozGetUserMedia) navigator.mozGetUserMedia({ video: true }, onGotStream, onNoStream)
- else if (navigator.webkitGetUserMedia) navigator.webkitGetUserMedia({ video: true }, onGotStream, onNoStream)
- else if (navigator.msGetUserMedia) navigator.msGetUserMedia({ video: true, audio: false }, onGotStream, onNoStream)
- else console.log('getUserMedia() not available from your Web browser!')
+ window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL
+ this.userMedia = this.userMedia && !!this.mediaDevices && !!window.URL
+
+ // Older versions of firefox (< 21) apparently claim support but user media does not actually work
+ if (navigator.userAgent.match(/Firefox\D+(\d+)/)) {
+ if (parseInt(RegExp.$1, 10) < 21) this.userMedia = null
}
+
+ // Make sure media stream is closed when navigating away from page
+ if (this.userMedia) {
+ window.addEventListener('beforeunload', (event) => {
+ this.reset()
+ })
+ }
+ }
+
+ start () {
+ this.userMedia = _userMedia === undefined ? this.userMedia : _userMedia
+
+ if (this.userMedia) {
+ // ask user for access to their camera
+ this.mediaDevices.getUserMedia({
+ audio: false,
+ video: true
+ })
+ .then((stream) => {
+ this.updateState({
+ videoStream: stream,
+ cameraReady: true
+ })
+ })
+ .catch((err) => {
+ if (this.opts.enableFlash && this.detectFlash()) {
+ console.log('up in the error catch')
+ console.log(this.detectFlash())
+ // setTimeout(() => {
+ // this.opts.forceFlash = 1
+ // this.attach(elem)
+ // }, 1)
+ } else {
+ console.log('Error:', err)
+ }
+ })
+ } else if (this.opts.enableFlash && this.detectFlash()) {
+ // flash fallback
+ // needed for flash-to-js interface
+ window.Webcam = Webcam
+ this.updateState({
+ useTheFlash: true
+ })
+ // elem.appendChild(div)
+ } else {
+ console.log('There was a problem!')
+ // this.dispatch('error', new WebcamError( this.params.noInterfaceFoundText ))
+ }
+ }
+
+ /**
+ * Detects if browser supports flash
+ * Code snippet borrowed from: https://github.com/swfobject/swfobject
+ *
+ * @return {bool} flash supported
+ */
+ detectFlash () {
+ var SHOCKWAVE_FLASH = 'Shockwave Flash'
+ var SHOCKWAVE_FLASH_AX = 'ShockwaveFlash.ShockwaveFlash'
+ var FLASH_MIME_TYPE = 'application/x-shockwave-flash'
+ var win = window
+ var nav = navigator
+ var hasFlash = false
+
+ if (typeof nav.plugins !== 'undefined' && typeof nav.plugins[SHOCKWAVE_FLASH] === 'object') {
+ var desc = nav.plugins[SHOCKWAVE_FLASH].description
+ if (desc && (typeof nav.mimeTypes !== 'undefined' && nav.mimeTypes[FLASH_MIME_TYPE] && nav.mimeTypes[FLASH_MIME_TYPE].enabledPlugin)) {
+ hasFlash = true
+ }
+ } else if (typeof win.ActiveXObject !== 'undefined') {
+ try {
+ var ax = new win.ActiveXObject(SHOCKWAVE_FLASH_AX)
+ if (ax) {
+ var ver = ax.GetVariable('$version')
+ if (ver) hasFlash = true
+ }
+ } catch (e) {}
+ }
+
+ return hasFlash
+ }
+
+ reset () {
+ // shutdown camera, reset to potentially attach again
+ if (this.preview_active) this.unfreeze()
+
+ if (this.userMedia) {
+ if (this.stream) {
+ if (this.stream.getVideoTracks) {
+ // get video track to call stop on it
+ var tracks = this.stream.getVideoTracks()
+ if (tracks && tracks[0] && tracks[0].stop) tracks[0].stop()
+ } else if (this.stream.stop) {
+ // deprecated, may be removed in future
+ this.stream.stop()
+ }
+ }
+ delete this.stream
+ delete this.video
+ }
+
+ if (this.userMedia !== true) {
+ // call for turn off camera in flash
+ this.getMovie()._releaseCamera()
+ }
+ }
+
+ getSWFHTML () {
+ // Return HTML for embedding flash based webcam capture movie
+ var swfURL = this.params.swfURL
+
+ // make sure we aren't running locally (flash doesn't work)
+ if (location.protocol.match(/file/)) {
+ return '
ERROR: the Webcam.js Flash fallback does not work from local disk. Please run it from a web server.
'
+ }
+
+ // make sure we have flash
+ if (!this.detectFlash()) {
+ return '
No flash
'
+ }
+
+ // set default swfURL if not explicitly set
+ if (!swfURL) {
+ // find our script tag, and use that base URL
+ var base_url = ''
+ var scpts = document.getElementsByTagName('script')
+ for (var idx = 0, len = scpts.length; idx < len; idx++) {
+ var src = scpts[idx].getAttribute('src')
+ if (src && src.match(/\/webcam(\.min)?\.js/)) {
+ base_url = src.replace(/\/webcam(\.min)?\.js.*$/, '')
+ idx = len
+ }
+ }
+ if (base_url) swfURL = base_url + '/webcam.swf'
+ else swfURL = 'webcam.swf'
+ }
+
+ // // if this is the user's first visit, set flashvar so flash privacy settings panel is shown first
+ // if (window.localStorage && !localStorage.getItem('visited')) {
+ // // this.params.new_user = 1
+ // localStorage.setItem('visited', 1)
+ // }
+ // this.params.new_user = 1
+ // construct flashvars string
+ var flashvars = ''
+ for (var key in this.params) {
+ if (flashvars) flashvars += '&'
+ flashvars += key + '=' + escape(this.params[key])
+ }
+
+ // construct object/embed tag
+
+ return html`
`
+ }
+
+ getMovie () {
+ // get reference to movie object/embed in DOM
+ var movie = document.getElementById('webcam_movie_obj')
+ if (!movie || !movie._snap) movie = document.getElementById('webcam_movie_embed')
+ if (!movie) console.log('getMovie error')
+ return movie
}
/**
@@ -114,56 +288,35 @@ export default class Webcam extends Plugin {
this.canvas = document.querySelector('.UppyWebcam-canvas')
}
- /**
- * When `startWebcam` successfully captures media,
- * this callback sets up video playback in the DOM.
- *
- * @param {MediaStream} stream user media stream
- */
- onGotStream (stream) {
- console.log('Got stream!')
- this.updateState({
- cameraReady: true
- })
- this.videoStream = stream
- this.mediaRecorder = new window.MediaRecorder(stream)
+ flashNotify (type, msg) {
+ // receive notification from flash about event
+ switch (type) {
+ case 'flashLoadComplete':
+ // movie loaded successfully
+ break
- const video = this.video
+ case 'cameraLive':
+ // camera is live and ready to snap
+ this.live = true
+ break
- console.log('Got stream.')
+ case 'error':
+ // Flash error
+ console.log('There was a flash error', msg)
+ break
- video.onerror = () => {
- console.log('video.onerror')
- if (video) {
- this.stopWebcam()
- }
+ default:
+ // catch-all event, just in case
+ console.log('webcam flash_notify: ' + type + ': ' + msg)
+ break
}
-
- stream.onended = this.onNoStream
-
- if (window.webkitURL) {
- video.src = window.webkitURL.createObjectURL(stream)
- } else if (video.mozSrcObject !== undefined) {
- video.mozSrcObject = stream
- video.play()
- } else if (navigator.mozGetUserMedia) {
- video.src = stream
- video.play()
- } else if (window.URL) {
- video.src = window.URL.createObjectURL(stream)
- } else {
- video.src = stream
- }
-
- this.video = document.querySelector('.UppyWebcam-video')
- this.canvas = document.querySelector('.UppyWebcam-canvas')
}
- /**
- * Error callback when capturing user media fails.
- */
- onNoStream () {
- console.log('Access to camera was denied!')
+ configure (panel) {
+ // open flash configuration panel -- specify tab name:
+ // 'camera', 'privacy', 'default', 'localStorage', 'microphone', 'settingsManager'
+ if (!panel) panel = 'camera'
+ this.getMovie()._configure(panel)
}
/**
@@ -260,13 +413,17 @@ export default class Webcam extends Plugin {
}
render (state) {
- if (!state.webcam.cameraReady) {
+ if (!state.webcam.cameraReady && !state.webcam.useTheFlash) {
return PermissionsScreen(state.webcam)
}
- return CameraScreen({
- onSnapshot: this.takeSnapshot
- })
+ const stream = state.webcam.videoStream ? URL.createObjectURL(state.webcam.videoStream) : null
+
+ return CameraScreen(extend(state.webcam, {
+ onSnapshot: this.takeSnapshot,
+ getSWFHTML: this.getSWFHTML,
+ src: stream
+ }))
}
focus () {
@@ -277,8 +434,8 @@ export default class Webcam extends Plugin {
setTimeout(function () {
firstInput.focus()
}, 10)
-
- this.startWebcam()
+ this.start()
+ // this.startWebcam()
}
install () {
diff --git a/src/plugins/index.js b/src/plugins/index.js
index 6217f1182..6f97b95f4 100644
--- a/src/plugins/index.js
+++ b/src/plugins/index.js
@@ -9,6 +9,7 @@ import Dummy from './Dummy'
import DragDrop from './DragDrop'
import Formtag from './Formtag'
import GoogleDrive from './GoogleDrive/index.js'
+import Webcam from './Webcam/index.js'
// Progressindicators
import ProgressBar from './ProgressBar'
@@ -36,5 +37,6 @@ export default {
Tus10,
Multipart,
// TransloaditBasic,
- Dashboard
+ Dashboard,
+ Webcam
}
diff --git a/website/src/examples/modal/app.es6 b/website/src/examples/modal/app.es6
index 570033899..8c4633863 100644
--- a/website/src/examples/modal/app.es6
+++ b/website/src/examples/modal/app.es6
@@ -4,7 +4,7 @@ import Tus10 from '../../../../src/plugins/Tus10.js'
import Dashboard from '../../../../src/plugins/Dashboard'
import GoogleDrive from '../../../../src/plugins/GoogleDrive'
import ProgressBar from '../../../../src/plugins/ProgressBar.js'
-import Webcam from '../../../../src/plugins/Webcam.js'
+import Webcam from '../../../../src/plugins/Webcam/index.js'
import MetaData from '../../../../src/plugins/MetaData.js'
import { UPPY_SERVER } from '../env'