mirror of
https://github.com/GuerillaStudio/souvenir.git
synced 2025-01-20 18:30:20 +00:00
refact(capture): rewrite with task
This commit is contained in:
parent
a2017f3f47
commit
1711911d62
4 changed files with 93 additions and 107 deletions
24
package-lock.json
generated
24
package-lock.json
generated
|
@ -3227,11 +3227,6 @@
|
||||||
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
|
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"eventemitter3": {
|
|
||||||
"version": "3.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz",
|
|
||||||
"integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA=="
|
|
||||||
},
|
|
||||||
"events": {
|
"events": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz",
|
||||||
|
@ -6076,18 +6071,11 @@
|
||||||
"os-tmpdir": "^1.0.0"
|
"os-tmpdir": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"p-event": {
|
|
||||||
"version": "4.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/p-event/-/p-event-4.1.0.tgz",
|
|
||||||
"integrity": "sha512-4vAd06GCsgflX4wHN1JqrMzBh/8QZ4j+rzp0cd2scXRwuBEv+QR3wrVA5aLhWDLw4y2WgDKvzWF3CCLmVM1UgA==",
|
|
||||||
"requires": {
|
|
||||||
"p-timeout": "^2.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"p-finally": {
|
"p-finally": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
|
||||||
"integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4="
|
"integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"p-locate": {
|
"p-locate": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
|
@ -6115,14 +6103,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"p-timeout": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz",
|
|
||||||
"integrity": "sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==",
|
|
||||||
"requires": {
|
|
||||||
"p-finally": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"package-json": {
|
"package-json": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz",
|
||||||
|
|
|
@ -12,12 +12,10 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.4.3",
|
"@babel/runtime": "^7.4.3",
|
||||||
"eventemitter3": "^3.1.0",
|
|
||||||
"folktale": "^2.3.2",
|
"folktale": "^2.3.2",
|
||||||
"generic-pool": "^3.6.1",
|
"generic-pool": "^3.6.1",
|
||||||
"gif-writer": "^0.9.3",
|
"gif-writer": "^0.9.3",
|
||||||
"objectFitPolyfill": "^2.1.1",
|
"objectFitPolyfill": "^2.1.1",
|
||||||
"p-event": "^4.0.0",
|
|
||||||
"postcss-modules": "^1.4.1",
|
"postcss-modules": "^1.4.1",
|
||||||
"vue": "^2.6.10",
|
"vue": "^2.6.10",
|
||||||
"vue-router": "^3.0.2",
|
"vue-router": "^3.0.2",
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import EventEmitter from 'eventemitter3'
|
import { task, of as taskOf } from 'folktale/concurrency/task'
|
||||||
import pEvent from 'p-event'
|
|
||||||
import { makeRectangle, crop } from '/services/rectangle.js'
|
import { makeRectangle, crop } from '/services/rectangle.js'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -8,88 +7,92 @@ import {
|
||||||
GIF_FRAME_RATE
|
GIF_FRAME_RATE
|
||||||
} from '/constants.js'
|
} from '/constants.js'
|
||||||
|
|
||||||
export function capture ({ mediaStream, facingMode }, duration) {
|
export function capture({ mediaStream, facingMode }, duration, progressCallback) {
|
||||||
const emitter = new EventEmitter()
|
const delayTime = 1000 / GIF_FRAME_RATE
|
||||||
|
const totalFrames = duration / 1000 * GIF_FRAME_RATE
|
||||||
|
|
||||||
Promise.resolve().then(async () => {
|
if (totalFrames < 1) {
|
||||||
const delayTime = 1000 / GIF_FRAME_RATE
|
return taskOf({
|
||||||
const totalFrames = duration / 1000 * GIF_FRAME_RATE
|
imageWidth: GIF_WIDTH,
|
||||||
|
imageHeight: GIF_HEIGHT,
|
||||||
// Well, this is a very low frame rate or very short duration clip
|
imageDataList: [],
|
||||||
if (totalFrames < 1) {
|
delayTime
|
||||||
emitter.emit('done', {
|
})
|
||||||
imageWidth: GIF_WIDTH,
|
}
|
||||||
imageHeight: GIF_HEIGHT,
|
|
||||||
imageDataList: [],
|
|
||||||
delayTime
|
|
||||||
})
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const imageDataList = []
|
|
||||||
|
|
||||||
const canvas = document.createElement('canvas')
|
|
||||||
canvas.width = GIF_WIDTH
|
|
||||||
canvas.height = GIF_HEIGHT
|
|
||||||
|
|
||||||
const destinationRectangle = makeRectangle(0, 0, canvas.width, canvas.height)
|
|
||||||
|
|
||||||
const canvasContext = canvas.getContext('2d')
|
|
||||||
|
|
||||||
if (facingMode === 'user' || facingMode === 'unknow') {
|
|
||||||
canvasContext.translate(destinationRectangle.width, 0)
|
|
||||||
canvasContext.scale(-1, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return task(resolver => {
|
||||||
const video = document.createElement('video')
|
const video = document.createElement('video')
|
||||||
video.setAttribute('playsinline', '')
|
video.setAttribute('playsinline', '')
|
||||||
video.setAttribute('webkit-playsinline', '')
|
video.setAttribute('webkit-playsinline', '')
|
||||||
video.srcObject = mediaStream
|
video.srcObject = mediaStream
|
||||||
video.play()
|
video.play()
|
||||||
|
|
||||||
await pEvent(video, 'canplaythrough')
|
video.addEventListener('canplaythrough', afterVideoAvailable, {
|
||||||
const soureRectangle = crop(makeRectangle(0, 0, video.videoWidth, video.videoHeight))
|
once: true
|
||||||
|
})
|
||||||
|
|
||||||
step()
|
resolver.cleanup(() => {
|
||||||
|
video.removeEventListener('canplaythrough', afterVideoAvailable)
|
||||||
|
})
|
||||||
|
|
||||||
function step () {
|
function afterVideoAvailable() {
|
||||||
canvasContext.drawImage(
|
const canvas = document.createElement('canvas')
|
||||||
video,
|
canvas.width = GIF_WIDTH
|
||||||
soureRectangle.x,
|
canvas.height = GIF_HEIGHT
|
||||||
soureRectangle.y,
|
|
||||||
soureRectangle.width,
|
|
||||||
soureRectangle.height,
|
|
||||||
destinationRectangle.x,
|
|
||||||
destinationRectangle.y,
|
|
||||||
destinationRectangle.width,
|
|
||||||
destinationRectangle.height
|
|
||||||
)
|
|
||||||
|
|
||||||
const imageData = canvasContext.getImageData(
|
const canvasContext = canvas.getContext('2d')
|
||||||
destinationRectangle.x,
|
|
||||||
destinationRectangle.y,
|
|
||||||
destinationRectangle.width,
|
|
||||||
destinationRectangle.height
|
|
||||||
)
|
|
||||||
|
|
||||||
imageDataList.push(imageData)
|
if (facingMode === 'user' || facingMode === 'unknow') {
|
||||||
|
canvasContext.translate(canvas.width, 0)
|
||||||
|
canvasContext.scale(-1, 1)
|
||||||
|
}
|
||||||
|
|
||||||
emitter.emit('progress', imageDataList.length / totalFrames)
|
const imageDataList = []
|
||||||
|
const destinationRectangle = makeRectangle(0, 0, canvas.width, canvas.height)
|
||||||
|
const soureRectangle = crop(makeRectangle(0, 0, video.videoWidth, video.videoHeight))
|
||||||
|
|
||||||
if (imageDataList.length < totalFrames) {
|
|
||||||
setTimeout(step, delayTime)
|
captureFrame()
|
||||||
} else {
|
|
||||||
emitter.emit('done', {
|
const intervalId = setInterval(captureFrame, delayTime)
|
||||||
imageDataList,
|
|
||||||
imageWidth: GIF_WIDTH,
|
resolver.cleanup(() => {
|
||||||
imageHeight: GIF_HEIGHT,
|
clearInterval(intervalId)
|
||||||
delayTime
|
})
|
||||||
})
|
|
||||||
|
function captureFrame () {
|
||||||
|
canvasContext.drawImage(
|
||||||
|
video,
|
||||||
|
soureRectangle.x,
|
||||||
|
soureRectangle.y,
|
||||||
|
soureRectangle.width,
|
||||||
|
soureRectangle.height,
|
||||||
|
destinationRectangle.x,
|
||||||
|
destinationRectangle.y,
|
||||||
|
destinationRectangle.width,
|
||||||
|
destinationRectangle.height
|
||||||
|
)
|
||||||
|
|
||||||
|
const imageData = canvasContext.getImageData(
|
||||||
|
destinationRectangle.x,
|
||||||
|
destinationRectangle.y,
|
||||||
|
destinationRectangle.width,
|
||||||
|
destinationRectangle.height
|
||||||
|
)
|
||||||
|
|
||||||
|
imageDataList.push(imageData)
|
||||||
|
|
||||||
|
progressCallback(imageDataList.length / totalFrames)
|
||||||
|
|
||||||
|
if (imageDataList.length >= totalFrames) {
|
||||||
|
resolver.resolve({
|
||||||
|
imageDataList,
|
||||||
|
imageWidth: GIF_WIDTH,
|
||||||
|
imageHeight: GIF_HEIGHT,
|
||||||
|
delayTime
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => emitter.emit('error', error))
|
|
||||||
|
|
||||||
return emitter
|
|
||||||
}
|
}
|
|
@ -119,23 +119,28 @@ export default {
|
||||||
},
|
},
|
||||||
runCapture () {
|
runCapture () {
|
||||||
this.capturing = true
|
this.capturing = true
|
||||||
const capturing = capture(this.camera, this.duration.selected * 1000)
|
|
||||||
|
|
||||||
capturing.once('error', error => {
|
const captureExecution = capture(
|
||||||
console.error(error)
|
this.camera,
|
||||||
|
this.duration.selected * 1000,
|
||||||
|
value => {
|
||||||
|
this.capturingProgress = value
|
||||||
|
}
|
||||||
|
).run()
|
||||||
|
|
||||||
|
const cleanup = () => {
|
||||||
this.capturing = false
|
this.capturing = false
|
||||||
this.capturingProgress = 0
|
this.capturingProgress = 0
|
||||||
})
|
}
|
||||||
|
|
||||||
capturing.on('progress', value => {
|
captureExecution.listen({
|
||||||
this.capturingProgress = value
|
onCancelled: cleanup,
|
||||||
})
|
onRejected: cleanup,
|
||||||
|
onResolved: (captureData) => {
|
||||||
capturing.once('done', captureData => {
|
cleanup()
|
||||||
this.capturing = false
|
this.$store.commit('updateCapture', captureData)
|
||||||
this.capturingProgress = 0
|
this.$router.push({ name: 'preview' })
|
||||||
this.$store.commit('updateCapture', captureData)
|
}
|
||||||
this.$router.push({ name: 'preview' })
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
async ensureCamera () {
|
async ensureCamera () {
|
||||||
|
|
Loading…
Reference in a new issue