Refactor graph data flow, re-add subsection looping and seeking (#19)
* Close web workers when they're done * Have graphs generate their own data, insert new data better * Replace all messages in graphs to handle out of order signals * Fix bugs, add video looping * Fix looping a bit, optimizations * Fix test cases * Remove debug statements * Make jest comma package workaround more genericmain
parent
58c77f2aca
commit
edf84da5ab
17
.babelrc
17
.babelrc
|
@ -7,10 +7,23 @@
|
|||
"targets": {
|
||||
"browsers": ["last 2 versions", "safari >= 7"]
|
||||
},
|
||||
useBuiltIns: "usage"
|
||||
"useBuiltIns": "usage"
|
||||
}
|
||||
],
|
||||
"stage-0"
|
||||
],
|
||||
"plugins": ["emotion"]
|
||||
"plugins": ["emotion"],
|
||||
"env": {
|
||||
"test": {
|
||||
"presets": [
|
||||
[
|
||||
"env",
|
||||
{
|
||||
"modules": false
|
||||
}
|
||||
],
|
||||
"stage-0"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"private": true,
|
||||
"homepage": "https://community.comma.ai/cabana",
|
||||
"dependencies": {
|
||||
"@commaai/comma-api": "^1.1.2",
|
||||
"@commaai/comma-api": "^1.1.4",
|
||||
"@commaai/hls.js": "^0.12.2",
|
||||
"@commaai/log_reader": "^0.3.1",
|
||||
"@commaai/my-comma-auth": "^1.1.0",
|
||||
|
@ -90,5 +90,10 @@
|
|||
"*.{graphql,gql}": ["prettier --parser graphql --write", "git add"],
|
||||
"*.{md,markdown}": ["prettier --parser markdown --write", "git add"],
|
||||
"*.scss": ["prettier --parser postcss --write", "git add"]
|
||||
},
|
||||
"jest": {
|
||||
"moduleNameMapper": {
|
||||
"^@commaai/(.*)$": "<rootDir>/node_modules/@commaai/$1/dist/index.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -673,8 +673,14 @@ export default class CanExplorer extends Component {
|
|||
currentParts = [minPart, maxPart];
|
||||
currentPart = part;
|
||||
|
||||
// update state then load new parts
|
||||
this.setState({ currentParts, currentPart }, this.partChangeDebounced);
|
||||
if (
|
||||
currentPart !== this.state.currentPart ||
|
||||
currentParts[0] !== this.state.currentParts[0] ||
|
||||
currentParts[1] !== this.state.currentParts[1]
|
||||
) {
|
||||
// update state then load new parts
|
||||
this.setState({ currentParts, currentPart }, this.partChangeDebounced);
|
||||
}
|
||||
}
|
||||
|
||||
showEditMessageModal(msgKey) {
|
||||
|
|
|
@ -6,7 +6,7 @@ import { shallow, mount, render } from "enzyme";
|
|||
|
||||
test("PartSelector successfully mounts with minimal default props", () => {
|
||||
const component = shallow(
|
||||
<PartSelector onPartChange={() => {}} partsCount={0} />
|
||||
<PartSelector onPartChange={() => {}} partsCount={0} selectedPart={0} />
|
||||
);
|
||||
expect(component.exists()).toBe(true);
|
||||
});
|
||||
|
|
|
@ -9,7 +9,7 @@ test("RouteSeeker successfully mounts with minimal default props", () => {
|
|||
<RouteSeeker
|
||||
nearestFrameTime={0}
|
||||
segmentProgress={() => {}}
|
||||
secondsLoaded={0}
|
||||
videoLength={0}
|
||||
segmentIndices={[]}
|
||||
onUserSeek={() => {}}
|
||||
onPlaySeek={() => {}}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
global.__JEST__ = 1;
|
||||
|
||||
import API from "@commaai/comma-api";
|
||||
import RouteVideoSync from "../../components/RouteVideoSync";
|
||||
import React from "react";
|
||||
import { shallow, mount, render } from "enzyme";
|
||||
|
@ -20,9 +21,11 @@ test("RouteVideoSync successfully mounts with minimal default props", () => {
|
|||
message={null}
|
||||
secondsLoaded={0}
|
||||
startOffset={0}
|
||||
segment={[]}
|
||||
seekIndex={0}
|
||||
userSeekIndex={0}
|
||||
playing={false}
|
||||
playSpeed={1}
|
||||
url={"http://comma.ai"}
|
||||
canFrameOffset={0}
|
||||
firstCanTime={0}
|
||||
|
|
|
@ -4,7 +4,9 @@ import PropTypes from "prop-types";
|
|||
import cx from "classnames";
|
||||
|
||||
import Signal from "../models/can/signal";
|
||||
import GraphData from "../models/graph-data";
|
||||
import CanPlot from "../vega/CanPlot";
|
||||
import debounce from "../utils/debounce";
|
||||
|
||||
const DefaultPlotInnerStyle = {
|
||||
position: "absolute",
|
||||
|
@ -16,7 +18,7 @@ export default class CanGraph extends Component {
|
|||
static emptyTable = [];
|
||||
|
||||
static propTypes = {
|
||||
data: PropTypes.object,
|
||||
plottedSignal: PropTypes.string,
|
||||
messages: PropTypes.object,
|
||||
messageId: PropTypes.string,
|
||||
messageName: PropTypes.string,
|
||||
|
@ -43,7 +45,8 @@ export default class CanGraph extends Component {
|
|||
shiftX: 0,
|
||||
shiftY: 0,
|
||||
bounds: null,
|
||||
isDataInserted: false
|
||||
isDataInserted: false,
|
||||
data: this.getGraphData(props)
|
||||
};
|
||||
this.onNewView = this.onNewView.bind(this);
|
||||
this.onSignalClickTime = this.onSignalClickTime.bind(this);
|
||||
|
@ -52,6 +55,40 @@ export default class CanGraph extends Component {
|
|||
this.onDragAnchorMouseUp = this.onDragAnchorMouseUp.bind(this);
|
||||
this.onDragStart = this.onDragStart.bind(this);
|
||||
this.onPlotResize = this.onPlotResize.bind(this);
|
||||
this.insertData = this.insertData.bind(this);
|
||||
}
|
||||
|
||||
getGraphData(props) {
|
||||
let firstRelTime = -1;
|
||||
let lastRelTime = -1;
|
||||
let series = props.plottedSignals
|
||||
.map(signals => {
|
||||
const { messageId, signalUid } = signals;
|
||||
let entries = props.messages[messageId].entries;
|
||||
if (entries.length) {
|
||||
let messageRelTime = entries[0].relTime;
|
||||
if (firstRelTime === -1) {
|
||||
firstRelTime = messageRelTime;
|
||||
} else {
|
||||
firstRelTime = Math.min(firstRelTime, messageRelTime);
|
||||
}
|
||||
messageRelTime = entries[entries.length - 1].relTime;
|
||||
lastRelTime = Math.max(lastRelTime, messageRelTime);
|
||||
}
|
||||
return GraphData._calcGraphData(
|
||||
props.messages[messageId],
|
||||
signalUid,
|
||||
0
|
||||
);
|
||||
})
|
||||
.reduce((m, v) => m.concat(v), []);
|
||||
|
||||
return {
|
||||
updated: Date.now(),
|
||||
series,
|
||||
firstRelTime,
|
||||
lastRelTime
|
||||
};
|
||||
}
|
||||
|
||||
segmentIsNew(newSegment) {
|
||||
|
@ -61,14 +98,6 @@ export default class CanGraph extends Component {
|
|||
);
|
||||
}
|
||||
|
||||
dataChanged(prevProps, nextProps) {
|
||||
return (
|
||||
nextProps.data.series.length !== prevProps.data.series.length ||
|
||||
!prevProps.signalSpec.equals(nextProps.signalSpec) ||
|
||||
nextProps.data.updated !== this.props.data.updated
|
||||
);
|
||||
}
|
||||
|
||||
visualChanged(prevProps, nextProps) {
|
||||
return (
|
||||
prevProps.canReceiveGraphDrop !== nextProps.canReceiveGraphDrop ||
|
||||
|
@ -79,7 +108,6 @@ export default class CanGraph extends Component {
|
|||
onPlotResize({ bounds }) {
|
||||
this.setState({ bounds });
|
||||
|
||||
this.view.run();
|
||||
this.view.signal("width", bounds.width - 70);
|
||||
this.view.signal("height", 0.4 * (bounds.width - 70)); // 5:2 aspect ratio
|
||||
this.view.run();
|
||||
|
@ -87,48 +115,51 @@ export default class CanGraph extends Component {
|
|||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
if (this.view) {
|
||||
// only update if segment is new
|
||||
let segmentChanged = false;
|
||||
if (this.segmentIsNew(nextProps.segment)) {
|
||||
if (nextProps.segment.length > 0) {
|
||||
// Set segmented domain
|
||||
this.view.signal("segment", nextProps.segment);
|
||||
} else {
|
||||
// Reset segment to full domain
|
||||
this.view.signal("segment", 0);
|
||||
this.view.runAfter(() => {
|
||||
// only update if segment is new
|
||||
let segmentChanged = false;
|
||||
if (this.segmentIsNew(nextProps.segment)) {
|
||||
if (nextProps.segment.length > 0) {
|
||||
// Set segmented domain
|
||||
this.view.signal("segment", nextProps.segment);
|
||||
} else {
|
||||
// Reset segment to full domain
|
||||
this.view.signal("segment", 0);
|
||||
}
|
||||
segmentChanged = true;
|
||||
}
|
||||
segmentChanged = true;
|
||||
}
|
||||
|
||||
if (!nextProps.live && nextProps.currentTime !== this.props.currentTime) {
|
||||
this.view.signal("videoTime", nextProps.currentTime);
|
||||
segmentChanged = true;
|
||||
}
|
||||
if (
|
||||
!nextProps.live &&
|
||||
nextProps.currentTime !== this.props.currentTime
|
||||
) {
|
||||
this.view.signal("videoTime", nextProps.currentTime);
|
||||
segmentChanged = true;
|
||||
}
|
||||
|
||||
if (segmentChanged) {
|
||||
this.view.run();
|
||||
}
|
||||
if (segmentChanged) {
|
||||
this.view.runAsync();
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const dataChanged = this.dataChanged(this.props, nextProps);
|
||||
|
||||
return (
|
||||
dataChanged ||
|
||||
JSON.stringify(this.state) !== JSON.stringify(nextState) ||
|
||||
this.visualChanged(this.props, nextProps)
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
insertData() {
|
||||
this.view.remove("table", () => true).run();
|
||||
this.view.insert("table", this.props.data.series).run();
|
||||
}
|
||||
insertData = debounce(() => {
|
||||
let { series } = this.state.data;
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (this.dataChanged(prevProps, this.props)) {
|
||||
this.insertData();
|
||||
}
|
||||
}
|
||||
// adding plot points by diff isn't faster since it basically has to be n^2
|
||||
// out-of-order events make it so that you can't just check the bounds
|
||||
let changeset = this.view
|
||||
.changeset()
|
||||
.remove(v => true)
|
||||
.insert(series);
|
||||
this.view.change("table", changeset);
|
||||
this.view.run();
|
||||
}, 250);
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (
|
||||
|
@ -139,6 +170,21 @@ export default class CanGraph extends Component {
|
|||
} else if (!nextProps.dragPos && this.state.plotInnerStyle !== null) {
|
||||
this.setState({ plotInnerStyle: null });
|
||||
}
|
||||
if (
|
||||
this.props.messages !== nextProps.messages ||
|
||||
this.props.plottedSignal !== nextProps.plottedSignal
|
||||
) {
|
||||
let data = this.getGraphData(nextProps);
|
||||
if (
|
||||
data.series.length === this.state.data.series.length &&
|
||||
data.firstRelTime === this.state.data.firstRelTime &&
|
||||
data.lastRelTime === this.state.data.lastRelTime
|
||||
) {
|
||||
// do nothing, the data didn't *actually* change
|
||||
} else {
|
||||
this.setState({ data }, this.insertData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateStyleFromDragPos({ left, top }) {
|
||||
|
@ -177,9 +223,8 @@ export default class CanGraph extends Component {
|
|||
this.view.runAfter(() => {
|
||||
const state = this.view.getState();
|
||||
state.subcontext[0].signals.brush = 0;
|
||||
this.view.setState(state).runAfter(() => {
|
||||
this.insertData();
|
||||
});
|
||||
this.view.setState(state);
|
||||
this.insertData();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -292,8 +337,7 @@ export default class CanGraph extends Component {
|
|||
className="cabana-explorer-visuals-plot-container"
|
||||
>
|
||||
<CanPlot
|
||||
logLevel={0}
|
||||
data={{ table: CanGraph.emptyTable }}
|
||||
logLevel={1}
|
||||
onNewView={this.onNewView}
|
||||
onSignalClickTime={this.onSignalClickTime}
|
||||
onSignalSegment={this.onSignalSegment}
|
||||
|
|
|
@ -9,7 +9,6 @@ export default class CanGraphList extends Component {
|
|||
static propTypes = {
|
||||
plottedSignals: PropTypes.array.isRequired,
|
||||
messages: PropTypes.object.isRequired,
|
||||
graphData: PropTypes.array.isRequired,
|
||||
onGraphTimeClick: PropTypes.func.isRequired,
|
||||
seekTime: PropTypes.number.isRequired,
|
||||
onSegmentChanged: PropTypes.func.isRequired,
|
||||
|
@ -153,7 +152,6 @@ export default class CanGraphList extends Component {
|
|||
signalSpec={Object.assign(Object.create(signal), signal)}
|
||||
onSegmentChanged={this.props.onSegmentChanged}
|
||||
segment={this.props.segment}
|
||||
data={this.props.graphData[index]}
|
||||
onRelativeTimeClick={this.props.onGraphTimeClick}
|
||||
currentTime={this.props.seekTime}
|
||||
onDragStart={this.onGraphDragStart}
|
||||
|
|
|
@ -38,7 +38,7 @@ export default class Explorer extends Component {
|
|||
segmentIndices: [],
|
||||
shouldShowAddSignal: true,
|
||||
userSeekIndex: 0,
|
||||
userSeekTime: props.currentParts[0] * 60,
|
||||
userSeekTime: 0,
|
||||
playing: props.autoplay,
|
||||
signals: {},
|
||||
playSpeed: 1
|
||||
|
@ -124,28 +124,20 @@ export default class Explorer extends Component {
|
|||
plottedSignals = plottedSignals
|
||||
.map(plot =>
|
||||
plot.filter(({ messageId, signalUid }, index) => {
|
||||
const messageExists =
|
||||
Object.keys(nextProps.messages).indexOf(messageId) !== -1;
|
||||
const messageExists = !!nextProps.messages[messageId];
|
||||
let signalExists = true;
|
||||
if (!messageExists) {
|
||||
graphData.splice(index, 1);
|
||||
} else {
|
||||
if (messageExists) {
|
||||
signalExists = Object.values(
|
||||
nextProps.messages[messageId].frame.signals
|
||||
).some(signal => signal.uid === signalUid);
|
||||
|
||||
if (!signalExists) {
|
||||
graphData[index].series = graphData[index].series.filter(
|
||||
entry => entry.signalUid !== signalUid
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return messageExists && signalExists;
|
||||
})
|
||||
)
|
||||
.filter(plot => plot.length > 0);
|
||||
this.setState({ plottedSignals, graphData });
|
||||
|
||||
this.setState({ plottedSignals });
|
||||
|
||||
if (
|
||||
nextProps.selectedMessage &&
|
||||
|
@ -228,15 +220,6 @@ export default class Explorer extends Component {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (partsDidChange) {
|
||||
const { userSeekTime } = this.state;
|
||||
const nextSeekTime =
|
||||
userSeekTime -
|
||||
this.props.currentParts[0] * 60 +
|
||||
nextProps.currentParts[0] * 60;
|
||||
this.setState({ userSeekTime: nextSeekTime });
|
||||
}
|
||||
}
|
||||
|
||||
changePlaySpeed(value) {
|
||||
|
@ -356,18 +339,10 @@ export default class Explorer extends Component {
|
|||
const { segment, segmentIndices } = this.state;
|
||||
const { messages, selectedMessage } = this.props;
|
||||
if (segment.length > 0 || segmentIndices.length > 0) {
|
||||
let userSeekTime = 0;
|
||||
if (
|
||||
messages[selectedMessage] &&
|
||||
messages[selectedMessage].entries.length > 0
|
||||
) {
|
||||
userSeekTime = messages[selectedMessage].entries[0].relTime;
|
||||
}
|
||||
this.setState({
|
||||
segment: [],
|
||||
segmentIndices: [],
|
||||
userSeekIndex: 0,
|
||||
userSeekTime
|
||||
userSeekIndex: 0
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -387,8 +362,13 @@ export default class Explorer extends Component {
|
|||
if (entries.length === 0) return null;
|
||||
|
||||
const { segmentIndices } = this.state;
|
||||
if (segmentIndices.length === 2) {
|
||||
for (let i = segmentIndices[0]; i <= segmentIndices[1]; i++) {
|
||||
if (segmentIndices.length === 2 && segmentIndices[0] >= 0) {
|
||||
for (
|
||||
let i = segmentIndices[0],
|
||||
l = Math.min(entries.length - 1, segmentIndices[1]);
|
||||
i <= l;
|
||||
i++
|
||||
) {
|
||||
if (entries[i].relTime >= time) {
|
||||
return i;
|
||||
}
|
||||
|
@ -407,22 +387,9 @@ export default class Explorer extends Component {
|
|||
onUserSeek(time) {
|
||||
this.setState({ userSeekTime: time });
|
||||
const message = this.props.messages[this.props.selectedMessage];
|
||||
if (!message) {
|
||||
this.props.onUserSeek(time);
|
||||
return;
|
||||
}
|
||||
|
||||
const { entries } = message;
|
||||
const userSeekIndex = this.indexFromSeekTime(time);
|
||||
if (userSeekIndex) {
|
||||
const seekTime = entries[userSeekIndex].relTime;
|
||||
|
||||
this.setState({ userSeekIndex, userSeekTime: seekTime });
|
||||
this.props.onSeek(userSeekIndex, seekTime);
|
||||
} else {
|
||||
this.props.onUserSeek(time);
|
||||
this.setState({ userSeekTime: time });
|
||||
}
|
||||
this.props.onUserSeek(time);
|
||||
this.setState({ userSeekTime: time });
|
||||
}
|
||||
|
||||
onPlaySeek(time) {
|
||||
|
@ -439,20 +406,7 @@ export default class Explorer extends Component {
|
|||
}
|
||||
|
||||
onGraphTimeClick(messageId, time) {
|
||||
const canTime = time + this.props.firstCanTime;
|
||||
|
||||
const { entries } = this.props.messages[messageId];
|
||||
if (entries.length) {
|
||||
const userSeekIndex = Entries.findTimeIndex(entries, canTime);
|
||||
|
||||
this.props.onUserSeek(time);
|
||||
this.setState({
|
||||
userSeekIndex,
|
||||
userSeekTime: time
|
||||
});
|
||||
} else {
|
||||
this.setState({ userSeekTime: time });
|
||||
}
|
||||
this.onUserSeek(time);
|
||||
}
|
||||
|
||||
onPlay() {
|
||||
|
@ -471,34 +425,6 @@ export default class Explorer extends Component {
|
|||
return this.props.partsCount * 60;
|
||||
}
|
||||
|
||||
startOffset() {
|
||||
return 0;
|
||||
const partOffset = this.props.currentParts[0] * 60;
|
||||
const message = this.props.messages[this.props.selectedMessage];
|
||||
if (!message || message.entries.length === 0) {
|
||||
return partOffset;
|
||||
}
|
||||
|
||||
const { entries } = message;
|
||||
const { segment } = this.state;
|
||||
let startTime;
|
||||
if (segment.length === 2) {
|
||||
startTime = segment[0];
|
||||
} else {
|
||||
startTime = entries[0].relTime;
|
||||
}
|
||||
|
||||
if (
|
||||
startTime > partOffset &&
|
||||
startTime < (this.props.currentParts[1] + 1) * 60
|
||||
) {
|
||||
// startTime is within bounds of currently selected parts
|
||||
return startTime;
|
||||
} else {
|
||||
return partOffset;
|
||||
}
|
||||
}
|
||||
|
||||
onVideoClick() {
|
||||
const playing = !this.state.playing;
|
||||
this.setState({ playing });
|
||||
|
@ -651,8 +577,7 @@ export default class Explorer extends Component {
|
|||
<br />
|
||||
<RouteVideoSync
|
||||
message={this.props.messages[this.props.selectedMessage]}
|
||||
secondsLoaded={this.secondsLoaded()}
|
||||
startOffset={this.startOffset()}
|
||||
segment={this.state.segment}
|
||||
seekIndex={this.props.seekIndex}
|
||||
userSeekIndex={this.state.userSeekIndex}
|
||||
playing={this.state.playing}
|
||||
|
@ -682,7 +607,6 @@ export default class Explorer extends Component {
|
|||
<CanGraphList
|
||||
plottedSignals={this.state.plottedSignals}
|
||||
messages={this.props.messages}
|
||||
graphData={this.state.graphData}
|
||||
onGraphTimeClick={this.onGraphTimeClick}
|
||||
seekTime={this.props.seekTime}
|
||||
onSegmentChanged={this.onSegmentChanged}
|
||||
|
|
|
@ -66,6 +66,7 @@ export default class HLS extends Component {
|
|||
this.player.loadSource(source);
|
||||
this.player.attachMedia(this.videoElement);
|
||||
this.props.onVideoElementAvailable(this.videoElement);
|
||||
this.videoElement.currentTime = this.props.startTime;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import debounce from "../../utils/debounce";
|
|||
|
||||
export default class RouteSeeker extends Component {
|
||||
static propTypes = {
|
||||
secondsLoaded: PropTypes.number.isRequired,
|
||||
videoLength: PropTypes.number.isRequired,
|
||||
segmentIndices: PropTypes.arrayOf(PropTypes.number),
|
||||
onUserSeek: PropTypes.func,
|
||||
onPlaySeek: PropTypes.func,
|
||||
|
@ -58,10 +58,10 @@ export default class RouteSeeker extends Component {
|
|||
markerStyle: RouteSeeker.hiddenMarkerStyle,
|
||||
ratio: 0
|
||||
});
|
||||
} else if (nextProps.secondsLoaded !== this.props.secondsLoaded) {
|
||||
// adjust ratio in line with new secondsLoaded
|
||||
const secondsSeeked = ratio * this.props.secondsLoaded;
|
||||
const newRatio = secondsSeeked / nextProps.secondsLoaded;
|
||||
} else if (nextProps.videoLength !== this.props.videoLength) {
|
||||
// adjust ratio in line with new videoLength
|
||||
const secondsSeeked = ratio * this.props.videoLength;
|
||||
const newRatio = secondsSeeked / nextProps.videoLength;
|
||||
this.updateSeekedBar(newRatio);
|
||||
}
|
||||
|
||||
|
@ -158,17 +158,24 @@ export default class RouteSeeker extends Component {
|
|||
return;
|
||||
}
|
||||
|
||||
let { videoLength, startTime } = this.props;
|
||||
let { currentTime, duration } = videoElement;
|
||||
let newRatio = currentTime / duration;
|
||||
|
||||
currentTime = roundTime(currentTime);
|
||||
startTime = roundTime(startTime);
|
||||
videoLength = roundTime(videoLength);
|
||||
duration = roundTime(duration);
|
||||
|
||||
let newRatio = (currentTime - startTime) / videoLength;
|
||||
|
||||
if (newRatio === this.state.ratio) {
|
||||
this.playTimer = window.requestAnimationFrame(this.executePlayTimer);
|
||||
return;
|
||||
}
|
||||
|
||||
if (newRatio >= 1) {
|
||||
if (newRatio >= 1 || newRatio < 0) {
|
||||
newRatio = 0;
|
||||
currentTime = 0;
|
||||
currentTime = startTime;
|
||||
this.props.onUserSeek(newRatio);
|
||||
}
|
||||
|
||||
|
@ -236,3 +243,7 @@ export default class RouteSeeker extends Component {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
function roundTime(time) {
|
||||
return Math.round(time * 1000) / 1000;
|
||||
}
|
||||
|
|
|
@ -46,8 +46,7 @@ const Styles = StyleSheet.create({
|
|||
export default class RouteVideoSync extends Component {
|
||||
static propTypes = {
|
||||
userSeekIndex: PropTypes.number.isRequired,
|
||||
secondsLoaded: PropTypes.number.isRequired,
|
||||
startOffset: PropTypes.number.isRequired,
|
||||
segment: PropTypes.array.isRequired,
|
||||
message: PropTypes.object,
|
||||
canFrameOffset: PropTypes.number.isRequired,
|
||||
url: PropTypes.string.isRequired,
|
||||
|
@ -56,7 +55,8 @@ export default class RouteVideoSync extends Component {
|
|||
onUserSeek: PropTypes.func.isRequired,
|
||||
onPlay: PropTypes.func.isRequired,
|
||||
onPause: PropTypes.func.isRequired,
|
||||
userSeekTime: PropTypes.number.isRequired
|
||||
userSeekTime: PropTypes.number.isRequired,
|
||||
playSpeed: PropTypes.number.isRequired
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
|
@ -87,6 +87,14 @@ export default class RouteVideoSync extends Component {
|
|||
) {
|
||||
this.setState({ shouldRestartHls: true });
|
||||
}
|
||||
if (
|
||||
nextProps.userSeekTime &&
|
||||
this.props.userSeekTime !== nextProps.userSeekTime
|
||||
) {
|
||||
if (this.state.videoElement) {
|
||||
this.state.videoElement.currentTime = nextProps.userSeekTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nearestFrameUrl() {
|
||||
|
@ -121,20 +129,40 @@ export default class RouteVideoSync extends Component {
|
|||
});
|
||||
}
|
||||
|
||||
segmentProgress(currentTime) {
|
||||
// returns progress as number in [0,1]
|
||||
|
||||
if (currentTime < this.props.startOffset) {
|
||||
currentTime = this.props.startOffset;
|
||||
videoLength() {
|
||||
if (this.props.segment.length) {
|
||||
return this.props.segment[1] - this.props.segment[0];
|
||||
}
|
||||
|
||||
const ratio =
|
||||
(currentTime - this.props.startOffset) / this.props.secondsLoaded;
|
||||
if (this.state.videoElement) {
|
||||
return this.state.videoElement.duration;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
startTime() {
|
||||
if (this.props.segment.length) {
|
||||
return this.props.segment[0];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
segmentProgress(currentTime) {
|
||||
// returns progress as number in [0,1]
|
||||
let startTime = this.startTime();
|
||||
|
||||
if (currentTime < startTime) {
|
||||
currentTime = startTime;
|
||||
}
|
||||
|
||||
const ratio = (currentTime - startTime) / this.videoLength();
|
||||
return Math.max(0, Math.min(1, ratio));
|
||||
}
|
||||
|
||||
ratioTime(ratio) {
|
||||
return ratio * this.props.secondsLoaded + this.props.startOffset;
|
||||
return ratio * this.videoLength() + this.startTime();
|
||||
}
|
||||
|
||||
onVideoElementAvailable(videoElement) {
|
||||
|
@ -145,7 +173,11 @@ export default class RouteVideoSync extends Component {
|
|||
/* ratio in [0,1] */
|
||||
|
||||
let { videoElement } = this.state;
|
||||
let seekTime = videoElement.duration * ratio;
|
||||
if (isNaN(videoElement.duration)) {
|
||||
this.setState({ shouldRestartHls: true }, funcSeekToRatio);
|
||||
return;
|
||||
}
|
||||
let seekTime = this.ratioTime(ratio);
|
||||
videoElement.currentTime = seekTime;
|
||||
|
||||
const funcSeekToRatio = () => this.props.onUserSeek(seekTime);
|
||||
|
@ -177,7 +209,8 @@ export default class RouteVideoSync extends Component {
|
|||
this.props.url,
|
||||
process.env.REACT_APP_VIDEO_CDN
|
||||
).getRearCameraStreamIndexUrl()}
|
||||
startTime={this.props.userSeekTime}
|
||||
startTime={this.startTime()}
|
||||
videoLength={this.videoLength()}
|
||||
playbackSpeed={this.props.playSpeed}
|
||||
onVideoElementAvailable={this.onVideoElementAvailable}
|
||||
playing={this.props.playing}
|
||||
|
@ -194,7 +227,8 @@ export default class RouteVideoSync extends Component {
|
|||
className={css(Styles.seekBar)}
|
||||
nearestFrameTime={this.props.userSeekTime}
|
||||
segmentProgress={this.segmentProgress}
|
||||
secondsLoaded={this.props.secondsLoaded}
|
||||
startTime={this.startTime()}
|
||||
videoLength={this.videoLength()}
|
||||
segmentIndices={this.props.segmentIndices}
|
||||
onUserSeek={this.onUserSeek}
|
||||
onPlaySeek={this.props.onPlaySeek}
|
||||
|
|
|
@ -24,7 +24,7 @@ export const LOGENTRIES_TOKEN = "4bc98019-8277-4fe0-867c-ed21ea843cc5";
|
|||
|
||||
export const PART_SEGMENT_LENGTH = 3;
|
||||
|
||||
export const CAN_GRAPH_MAX_POINTS = 10000;
|
||||
export const CAN_GRAPH_MAX_POINTS = 5000;
|
||||
|
||||
export const STREAMING_WINDOW = 60;
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ function _calcGraphData(msg, signalUid, firstCanTime) {
|
|||
// Always include last message entry, which faciliates graphData comparison
|
||||
samples.push(msg.entries[msg.entries.length - 1]);
|
||||
}
|
||||
// sorting these doesn't fix the phantom lines
|
||||
return samples
|
||||
.filter(e => e.signals[signal.name] !== undefined)
|
||||
.map(entry => {
|
||||
|
|
|
@ -56,15 +56,16 @@ function sendBatch(entry) {
|
|||
);
|
||||
});
|
||||
|
||||
if (entry.ended) {
|
||||
console.log("Sending finished");
|
||||
}
|
||||
|
||||
self.postMessage({
|
||||
newMessages: messages,
|
||||
maxByteStateChangeCount,
|
||||
isFinished: entry.ended
|
||||
});
|
||||
|
||||
if (entry.ended) {
|
||||
console.log("Sending finished");
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
async function loadData(entry) {
|
||||
|
|
|
@ -27,10 +27,10 @@
|
|||
joi-browser "^13.4.0"
|
||||
querystringify "^2.1.1"
|
||||
|
||||
"@commaai/comma-api@^1.1.2":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@commaai/comma-api/-/comma-api-1.1.2.tgz#2abf967b708f2d650c8e9cb0eecd5044a3a39da2"
|
||||
integrity sha512-Pp19FnHSimzkBlbJCgPObklZc2VZvtYrHVQoCcfbsZMYZaOEFolHek4lZ0l2XkCNB8n71QleBbK6xGY56/jb+Q==
|
||||
"@commaai/comma-api@^1.1.4":
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@commaai/comma-api/-/comma-api-1.1.4.tgz#75f727bfa43f6f446a341d1f9154f15334cfb79e"
|
||||
integrity sha512-S25QY2OwO1aO5HKtvJQUnCwDsfBhgj7+SANZG99kHvxfFxOwWzT/GcV7G/xEtEJ06OsJ+Ih8XoUFm7IxNc1bwA==
|
||||
dependencies:
|
||||
babel-runtime "^6.26.0"
|
||||
config-request "^0.5.1"
|
||||
|
|
Loading…
Reference in New Issue