qcamera stream

main
Andy Haden 2019-12-11 01:28:14 -08:00
parent 3ab69d1d15
commit e9bec22a98
7 changed files with 75 additions and 39 deletions

View File

@ -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",

View File

@ -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}

View File

@ -463,7 +463,6 @@ export default class Explorer extends Component {
(this.props.currentParts[1] + 1) * 60
];
}
return (
<div className="cabana-explorer">
<div className={cx('cabana-explorer-signals', signalsExpandedClass)}>
@ -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}

View File

@ -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;
}
}

View File

@ -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 (
<div className="cabana-explorer-visuals-camera">
{isLoading ? this.loadingOverlay() : null}
@ -206,27 +232,24 @@ export default class RouteVideoSync extends Component {
) : null}
<HLS
className={css(Styles.hls)}
source={VideoApi(
url,
process.env.REACT_APP_VIDEO_CDN
).getRearCameraStreamIndexUrl()}
source={this.state.source}
startTime={startTime || 0}
videoLength={this.videoLength()}
playbackSpeed={playSpeed}
onVideoElementAvailable={this.onVideoElementAvailable}
playing={playing}
onStartTimeAvailable={this.onStartTimeAvailable}
onClick={onVideoClick}
onLoadStart={this.onLoadStart}
onLoadEnd={this.onLoadEnd}
onUserSeek={this.onUserSeek}
onPlaySeek={this.onPlaySeek}
segmentProgress={this.segmentProgress}
/>
<RouteSeeker
className={css(Styles.seekBar)}
nearestFrameTime={userSeekTime}
segmentProgress={this.segmentProgress}
startTime={this.startTime()}
startTime={this.startTime() - this.state.offset}
videoLength={this.videoLength()}
segmentIndices={segmentIndices}
onUserSeek={this.onUserSeek}

View File

@ -28,7 +28,7 @@ function sendBatch(entry) {
entry.messages = {};
entry.thumbnails = [];
let { maxByteStateChangeCount } = entry.options;
let { maxByteStateChangeCount, routeInitTime } = entry.options;
const newMaxByteStateChangeCount = DbcUtils.findMaxByteStateChangeCount(
messages
);
@ -48,7 +48,8 @@ function sendBatch(entry) {
newMessages: messages,
newThumbnails: thumbnails,
isFinished: entry.ended,
maxByteStateChangeCount
maxByteStateChangeCount,
routeInitTime,
});
if (entry.ended) {
@ -93,7 +94,7 @@ function insertEventData(src, part, entry, logTime, getData) {
time: logTime,
address,
data: getData(),
timeStart: entry.options.canStartTime
timeStart: entry.options.routeInitTime
};
entry.messages[id].entries.push(msgEntry);
@ -124,7 +125,7 @@ function insertCanMessage(entry, logTime, msg) {
time: logTime,
address,
data: msg.Dat,
timeStart: entry.options.canStartTime
timeStart: entry.options.routeInitTime
};
entry.messages[id].entries.push(msgEntry);
@ -164,8 +165,8 @@ async function loadData(entry) {
}
if ('InitData' in msg) {
const monoTime = msg.LogMonoTime / 1e9;
if (entry.options.canStartTime == null) {
entry.options.canStartTime = monoTime;
if (entry.options.routeInitTime == null) {
entry.options.routeInitTime = monoTime;
}
} else if ('Can' in msg) {
const monoTime = msg.LogMonoTime / 1000000000;
@ -244,7 +245,7 @@ async function loadData(entry) {
partial(getThermalFreeSpace, msg.Thermal)
);
} else if ('Thumbnail' in msg) {
const monoTime = msg.LogMonoTime / 1000000000 - entry.options.canStartTime;
const monoTime = msg.LogMonoTime / 1000000000 - entry.options.routeInitTime;
const data = new Uint8Array(msg.Thumbnail.Thumbnail);
entry.thumbnails.push({ data, monoTime });
} else {

View File

@ -928,10 +928,20 @@
joi-browser "^13.4.0"
querystringify "^2.1.1"
"@commaai/hls.js@^0.12.5":
version "0.12.5"
resolved "https://registry.yarnpkg.com/@commaai/hls.js/-/hls.js-0.12.5.tgz#891e47124b4e9e2a1528463613bf415ff252d2ec"
integrity sha512-aZcwFrhHSGVpi76cHMjT1wQ6Pe0AVv3s0TdzNGFA6LpCb6WCFy2EF9H8QCtNlAviccYnAw4XQrJ+hHToUp4QnA==
"@commaai/comma-api@^1.3.1":
version "1.3.1"
resolved "https://registry.yarnpkg.com/@commaai/comma-api/-/comma-api-1.3.1.tgz#b9f94beb1a3a6f7257683025422bbce5455fc137"
integrity sha512-S2cosuSobuY+mlMxy2Dpt6pK2wrymNcbN3Xz6f8i6072H7t33RK3DQgtTByOPoz4iDuVVQrgAexkP3zwXOUm8Q==
dependencies:
babel-runtime "^6.26.0"
config-request "^0.5.1"
joi-browser "^13.4.0"
querystringify "^2.1.1"
"@commaai/hls.js@^0.12.6":
version "0.12.6"
resolved "https://registry.yarnpkg.com/@commaai/hls.js/-/hls.js-0.12.6.tgz#adffabb407b71e9bb3cd60e5e7309b3a9831a5f7"
integrity sha512-yJHlG5A8zlemSF6A88reyy5RnCcLrUT/qYqXwoRz2dk4O3bZ226EOvI801ngOaeaI/CUq3Hf/K24yXR0/g4WOA==
dependencies:
eventemitter3 "3.1.0"
url-toolkit "^2.1.6"
@ -6130,11 +6140,6 @@ hex-color-regex@^1.1.0:
resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e"
integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==
hls@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/hls/-/hls-0.0.1.tgz#3433be0d4b1305db312c966527fbb156e7ed79ca"
integrity sha1-NDO+DUsTBdsxLJZlJ/uxVufteco=
hmac-drbg@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"