index.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. const Plugin = require('../Plugin')
  2. const WebcamProvider = require('../../uppy-base/src/plugins/Webcam')
  3. const { extend,
  4. getFileTypeExtension,
  5. supportsMediaRecorder } = require('../../core/Utils')
  6. const WebcamIcon = require('./WebcamIcon')
  7. const CameraScreen = require('./CameraScreen')
  8. const PermissionsScreen = require('./PermissionsScreen')
  9. /**
  10. * Webcam
  11. */
  12. module.exports = class Webcam extends Plugin {
  13. constructor (core, opts) {
  14. super(core, opts)
  15. this.userMedia = true
  16. this.protocol = location.protocol.match(/https/i) ? 'https' : 'http'
  17. this.type = 'acquirer'
  18. this.id = 'Webcam'
  19. this.title = 'Webcam'
  20. this.icon = WebcamIcon
  21. // set default options
  22. const defaultOptions = {
  23. enableFlash: true,
  24. modes: [
  25. 'video-audio',
  26. 'video-only',
  27. 'audio-only',
  28. 'picture'
  29. ]
  30. }
  31. this.params = {
  32. swfURL: 'webcam.swf',
  33. width: 400,
  34. height: 300,
  35. dest_width: 800, // size of captured image
  36. dest_height: 600, // these default to width/height
  37. image_format: 'jpeg', // image format (may be jpeg or png)
  38. jpeg_quality: 90, // jpeg image quality from 0 (worst) to 100 (best)
  39. enable_flash: true, // enable flash fallback,
  40. force_flash: false, // force flash mode,
  41. flip_horiz: false, // flip image horiz (mirror mode)
  42. fps: 30, // camera frames per second
  43. upload_name: 'webcam', // name of file in upload post data
  44. constraints: null, // custom user media constraints,
  45. flashNotDetectedText: 'ERROR: No Adobe Flash Player detected. Webcam.js relies on Flash for browsers that do not support getUserMedia (like yours).',
  46. noInterfaceFoundText: 'No supported webcam interface found.',
  47. unfreeze_snap: true // Whether to unfreeze the camera after snap (defaults to true)
  48. }
  49. // merge default options with the ones set by user
  50. this.opts = Object.assign({}, defaultOptions, opts)
  51. this.install = this.install.bind(this)
  52. this.updateState = this.updateState.bind(this)
  53. this.render = this.render.bind(this)
  54. // Camera controls
  55. this.start = this.start.bind(this)
  56. this.stop = this.stop.bind(this)
  57. this.takeSnapshot = this.takeSnapshot.bind(this)
  58. this.startRecording = this.startRecording.bind(this)
  59. this.stopRecording = this.stopRecording.bind(this)
  60. this.webcam = new WebcamProvider(this.opts, this.params)
  61. this.webcamActive = false
  62. }
  63. start () {
  64. this.webcamActive = true
  65. this.webcam.start()
  66. .then((stream) => {
  67. this.stream = stream
  68. this.updateState({
  69. // videoStream: stream,
  70. cameraReady: true
  71. })
  72. })
  73. .catch((err) => {
  74. this.updateState({
  75. cameraError: err
  76. })
  77. })
  78. }
  79. startRecording () {
  80. // TODO We can check here if any of the mime types listed in the
  81. // mimeToExtensions map in Utils.js are supported, and prefer to use one of
  82. // those.
  83. // Right now we let the browser pick a type that it deems appropriate.
  84. this.recorder = new MediaRecorder(this.stream)
  85. this.recordingChunks = []
  86. this.recorder.addEventListener('dataavailable', (event) => {
  87. this.recordingChunks.push(event.data)
  88. })
  89. this.recorder.start()
  90. this.updateState({
  91. isRecording: true
  92. })
  93. }
  94. stopRecording () {
  95. return new Promise((resolve, reject) => {
  96. this.recorder.addEventListener('stop', () => {
  97. this.updateState({
  98. isRecording: false
  99. })
  100. const mimeType = this.recordingChunks[0].type
  101. const fileExtension = getFileTypeExtension(mimeType)
  102. if (!fileExtension) {
  103. reject(new Error(`Could not upload file: Unsupported media type "${mimeType}"`))
  104. return
  105. }
  106. const file = {
  107. source: this.id,
  108. name: `webcam-${Date.now()}.${fileExtension}`,
  109. type: mimeType,
  110. data: new Blob(this.recordingChunks, { type: mimeType })
  111. }
  112. this.core.emitter.emit('core:file-add', file)
  113. this.recordingChunks = null
  114. this.recorder = null
  115. resolve()
  116. })
  117. this.recorder.stop()
  118. })
  119. }
  120. stop () {
  121. this.stream.getAudioTracks().forEach((track) => {
  122. track.stop()
  123. })
  124. this.stream.getVideoTracks().forEach((track) => {
  125. track.stop()
  126. })
  127. this.webcamActive = false
  128. this.stream = null
  129. this.streamSrc = null
  130. }
  131. takeSnapshot () {
  132. const opts = {
  133. name: `webcam-${Date.now()}.jpg`,
  134. mimeType: 'image/jpeg'
  135. }
  136. const video = this.target.querySelector('.UppyWebcam-video')
  137. const image = this.webcam.getImage(video, opts)
  138. const tagFile = {
  139. source: this.id,
  140. name: opts.name,
  141. data: image.data,
  142. type: opts.mimeType
  143. }
  144. this.core.emitter.emit('core:file-add', tagFile)
  145. }
  146. render (state) {
  147. if (!this.webcamActive) {
  148. this.start()
  149. }
  150. if (!state.webcam.cameraReady && !state.webcam.useTheFlash) {
  151. return PermissionsScreen(state.webcam)
  152. }
  153. if (!this.streamSrc) {
  154. this.streamSrc = this.stream ? URL.createObjectURL(this.stream) : null
  155. }
  156. return CameraScreen(extend(state.webcam, {
  157. onSnapshot: this.takeSnapshot,
  158. onStartRecording: this.startRecording,
  159. onStopRecording: this.stopRecording,
  160. onFocus: this.focus,
  161. onStop: this.stop,
  162. modes: this.opts.modes,
  163. supportsRecording: supportsMediaRecorder(),
  164. recording: state.webcam.isRecording,
  165. getSWFHTML: this.webcam.getSWFHTML,
  166. src: this.streamSrc
  167. }))
  168. }
  169. focus () {
  170. setTimeout(() => {
  171. this.core.emitter.emit('informer', 'Smile!', 'warning', 2000)
  172. }, 1000)
  173. }
  174. install () {
  175. this.webcam.init()
  176. this.core.setState({
  177. webcam: {
  178. cameraReady: false
  179. }
  180. })
  181. const target = this.opts.target
  182. const plugin = this
  183. this.target = this.mount(target, plugin)
  184. }
  185. uninstall () {
  186. this.webcam.reset()
  187. this.unmount()
  188. }
  189. /**
  190. * Little shorthand to update the state with my new state
  191. */
  192. updateState (newState) {
  193. const {state} = this.core
  194. const webcam = Object.assign({}, state.webcam, newState)
  195. this.core.setState({webcam})
  196. }
  197. }