Files
@ 3ae1e7f8db23
Branch filter:
Location: light9/light9/web/light9-vidref-replay.js
3ae1e7f8db23
4.0 KiB
application/javascript
vidref playback smoothness, autodelete short clips, manual-delete clips, vidref keyboard shortcuts
Ignore-this: 6daccf686fd66561029f3252ed4dbafd
Ignore-this: 6daccf686fd66561029f3252ed4dbafd
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | import { LitElement, TemplateResult, html, css } from '/node_modules/lit-element/lit-element.js';
import debug from '/lib/debug/debug-build-es6.js';
import _ from '/lib/underscore/underscore-min-es6.js';
import { rounding } from '/node_modules/significant-rounding/index.js';
const log = debug('replay');
class Light9VidrefReplay extends LitElement {
static get properties() {
return {
uri: { type: String },
videoUrl: { type: String },
songToVideo: { type: Object },
videoTime: { type: Number },
outVideoCurrentTime: { type: Number },
timeErr: { type: Number },
playRate: { type: Number }
};
}
estimateRate() {
const n = this.songToVideo.length;
const x0 = Math.round(n * .3);
const x1 = Math.round(n * .6);
const pt0 = this.songToVideo[x0];
const pt1 = this.songToVideo[x1];
return (pt1[1] - pt0[1]) / (pt1[0] - pt0[0]);
}
setVideoTimeFromSongTime(songTime, isPlaying) {
if (!this.songToVideo || !this.outVideo || this.outVideo.readyState < 1) {
return;
}
const i = _.sortedIndex(this.songToVideo, [songTime],
(row) => { return row[0]; });
this.videoTime = this.songToVideo[Math.max(0, i - 1)][1];
this.outVideoCurrentTime = this.outVideo.currentTime;
if (isPlaying) {
if (this.outVideo.paused) {
this.outVideo.play();
this.setRate(this.estimateRate());
}
const err = this.outVideo.currentTime - this.videoTime;
this.timeErr = err;
if (Math.abs(err) > window.thresh) {
this.outVideo.currentTime = this.videoTime;
const p = window.p;
if (err > 0) {
this.setRate(this.playRate - err * p);
} else {
this.setRate(this.playRate - err * p);
}
}
} else {
this.outVideo.pause();
this.outVideoCurrentTime = this.outVideo.currentTime = this.videoTime;
this.timeErr = 0;
}
}
setRate(r) {
this.playRate = Math.max(.1, Math.min(4, r));
this.outVideo.playbackRate = this.playRate;
}
firstUpdated() {
this.outVideo = this.shadowRoot.querySelector('#replay');
this.playRate = this.outVideo.playbackRate = 1.0;
}
onDelete() {
const u = new URL(window.location.href);
u.pathname = '/vidref/clips'
u.searchParams.set('uri', this.uri);
fetch(u.toString(), {method: 'DELETE'}).then((resp) => {
let event = new CustomEvent('clips-changed', {detail: {}});
this.dispatchEvent(event);
});
}
static get styles() {
return css`
:host {
margin: 5px;
border: 2px solid #46a79f;
display: flex;
flex-direction: column;
}
div {
padding: 5px;
}
.num {
display: inline-block;
width: 4em;
color: #29ffa0;
}
a {
color: rgb(97, 97, 255);
}
`;
}
render() {
return html`
<video id="replay" src="${this.videoUrl}"></video>
<div>
take is <a href="${this.uri}">${this.uri}</a>
(${Object.keys(this.songToVideo).length} frames)
<button @click="${this.onDelete}">Delete</button>
</div>
<!-- here, put a little canvas showing what coverage we have with the
actual/goal time cursors -->
<div>
video time should be <span class="num">${this.videoTime} </span>
actual = <span class="num">${rounding(this.outVideoCurrentTime, 3)}</span>,
err = <span class="num">${rounding(this.timeErr, 3)} </span>
rate = <span class="num">${rounding(this.playRate, 3)}</span>
</div>
`;
}
}
customElements.define('light9-vidref-replay', Light9VidrefReplay);
window.thresh=.3
window.p=.3
|