diff --git a/package.json b/package.json index 060d924..f4456ac 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,8 @@ "private": true, "homepage": "https://community.comma.ai/cabana", "dependencies": { - "@commaai/comma-api": "^1.2.2", - "@commaai/hls.js": "^0.12.5", + "@commaai/comma-api": "^1.3.1", + "@commaai/hls.js": "^0.12.6", "@commaai/log_reader": "^0.5.5", "@commaai/my-comma-auth": "^1.1.4", "@commaai/pandajs": "^0.3.4", @@ -34,7 +34,6 @@ "font-awesome": "^4.7.0", "github-api": "^3.0.0", "global": "^4.3.2", - "hls": "0.0.1", "husky": "^0.14.3", "int64-buffer": "^0.1.9", "js-cookie": "^2.1.4", diff --git a/src/CanExplorer.js b/src/CanExplorer.js index 6dcf447..b43f585 100644 --- a/src/CanExplorer.js +++ b/src/CanExplorer.js @@ -54,6 +54,7 @@ export default class CanExplorer extends Component { selectedMessages: [], route: null, canFrameOffset: 0, + routeInitTime: 0, firstCanTime: null, lastBusTime: null, selectedMessage: null, @@ -494,12 +495,15 @@ export default class CanExplorer extends Component { } maxByteStateChangeCount = e.data.maxByteStateChangeCount; - const { newMessages, newThumbnails, isFinished } = e.data; + const { newMessages, newThumbnails, isFinished, routeInitTime } = e.data; if (maxByteStateChangeCount > this.state.maxByteStateChangeCount) { this.setState({ maxByteStateChangeCount }); } else { maxByteStateChangeCount = this.state.maxByteStateChangeCount; } + if (routeInitTime !== this.state.routeInitTime) { + this.setState({ routeInitTime }); + } this.addMessagesToDataCache(part, newMessages, newThumbnails); @@ -1366,6 +1370,7 @@ export default class CanExplorer extends Component { routeStartTime={ route ? route.start_time : Moment() } + routeInitTime={ this.state.routeInitTime } partsCount={route ? route.proclog : 0} /> ) : null} diff --git a/src/components/Explorer.js b/src/components/Explorer.js index c6684a3..d11aaff 100644 --- a/src/components/Explorer.js +++ b/src/components/Explorer.js @@ -463,7 +463,6 @@ export default class Explorer extends Component { (this.props.currentParts[1] + 1) * 60 ]; } - return (
@@ -488,8 +487,8 @@ export default class Explorer extends Component { userSeekIndex={this.state.userSeekIndex} playing={this.state.playing} url={this.props.url} - canFrameOffset={this.props.canFrameOffset} firstCanTime={this.props.firstCanTime} + routeInitTime={this.props.routeInitTime} onVideoClick={this.onVideoClick} onPlaySeek={this.onPlaySeek} onUserSeek={this.onUserSeek} diff --git a/src/components/HLS.js b/src/components/HLS.js index edab6b6..92895ca 100644 --- a/src/components/HLS.js +++ b/src/components/HLS.js @@ -9,11 +9,11 @@ export default class HLS extends Component { playbackSpeed: PropTypes.number.isRequired, playing: PropTypes.bool.isRequired, onVideoElementAvailable: PropTypes.func, + onStartTimeAvailable: PropTypes.func, onClick: PropTypes.func, onLoadStart: PropTypes.func, onLoadEnd: PropTypes.func, onPlaySeek: PropTypes.func, - segmentProgress: PropTypes.func, onRestart: PropTypes.func }; @@ -38,7 +38,12 @@ export default class HLS extends Component { componentDidMount() { this.player = new Hls({ enableWorker: false, - disablePtsDtsCorrectionInMp4Remux: true + disablePtsDtsCorrectionInMp4Remux: false, + debug: true + }); + this.player.on(Hls.Events.INIT_PTS_FOUND, (event, data) => { + const time = data.initPTS/90000; + this.props.onStartTimeAvailable(time); }); this.player.on(Hls.Events.ERROR, (event, data) => { if (data.fatal) { @@ -89,7 +94,6 @@ 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; } } diff --git a/src/components/RouteVideoSync.js b/src/components/RouteVideoSync.js index 00b1e63..821e6a9 100644 --- a/src/components/RouteVideoSync.js +++ b/src/components/RouteVideoSync.js @@ -50,6 +50,9 @@ export default class RouteVideoSync extends Component { shouldShowJpeg: true, isLoading: true, videoElement: null, + source: null, + videoStartTime: null, + offset: 0, }; this.onLoadStart = this.onLoadStart.bind(this); @@ -59,29 +62,51 @@ export default class RouteVideoSync extends Component { this.onUserSeek = this.onUserSeek.bind(this); this.onPlaySeek = this.onPlaySeek.bind(this); this.ratioTime = this.ratioTime.bind(this); + this.onStartTimeAvailable = this.onStartTimeAvailable.bind(this); + } + + componentWillMount() { + this.setState({source: VideoApi( + this.props.url, + process.env.REACT_APP_VIDEO_CDN + ).getQcameraStreamIndexUrl()}); } componentDidUpdate(prevProps) { const { userSeekTime } = this.props; - const { videoElement } = this.state; + const { videoElement, videoStartTime } = this.state; + if ( prevProps.userSeekTime && userSeekTime !== prevProps.userSeekTime ) { if (videoElement) { - videoElement.currentTime = userSeekTime; + videoElement.currentTime = userSeekTime - this.state.offset; } } + + if (!prevProps.routeInitTime && this.props.routeInitTime) { + this.updateOffset(); + } } onVideoElementAvailable(videoElement) { this.setState({ videoElement }); } + onStartTimeAvailable(videoStartTime) { + this.setState({ videoStartTime }, () => this.updateOffset()); + } + + updateOffset() { + if (this.props.routeInitTime && this.state.videoStartTime) { + this.setState({ offset: this.state.videoStartTime - this.props.routeInitTime }); + } + } onUserSeek(ratio) { /* ratio in [0,1] */ - const { videoElement } = this.state; + const { videoElement, videoStartTime } = this.state; const { onUserSeek } = this.props; const seekTime = this.ratioTime(ratio); const funcSeekToRatio = () => onUserSeek(seekTime); @@ -89,7 +114,7 @@ export default class RouteVideoSync extends Component { if (Number.isNaN(videoElement.duration)) { return; } - videoElement.currentTime = seekTime; + videoElement.currentTime = seekTime - this.state.offset; if (ratio !== 0) { funcSeekToRatio(); @@ -97,9 +122,8 @@ export default class RouteVideoSync extends Component { } onPlaySeek(offset) { - const { onPlaySeek } = this.props; - this.seekTime = offset; - onPlaySeek(offset); + this.seekTime = offset + this.state.offset; + this.props.onPlaySeek(this.seekTime); } onLoadStart() { @@ -182,7 +206,8 @@ export default class RouteVideoSync extends Component { const { isLoading, shouldShowJpeg, - videoElement + videoElement, + videoStartTime, } = this.state; const { userSeekTime, @@ -194,6 +219,7 @@ export default class RouteVideoSync extends Component { startTime, segment } = this.props; + return (
{isLoading ? this.loadingOverlay() : null} @@ -206,27 +232,24 @@ export default class RouteVideoSync extends Component { ) : null}