The open source OpenXR runtime
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

xrt: Support more than two cameras in a SLAM sink

authored by

Mateo de Mayo and committed by
Jakob Bornecrantz
52cac31d a93dc650

+90 -80
+7 -5
src/xrt/auxiliary/tracking/t_euroc_recorder.cpp
··· 274 274 xrt_frame *copy = nullptr; 275 275 u_frame_clone(src_frame, &copy); 276 276 277 - xrt_sink_push_frame(is_left ? er->writer_queues.left : er->writer_queues.right, copy); 277 + xrt_sink_push_frame(is_left ? er->writer_queues.cams[0] : er->writer_queues.cams[1], copy); 278 278 279 279 xrt_frame_reference(&copy, NULL); 280 280 } ··· 357 357 // First, make the public queues that will clone frames in memory so that 358 358 // original frames can be released as soon as possible. Not doing this could 359 359 // result in frame queues from the user being filled up. 360 - u_sink_queue_create(xfctx, 0, &er->cloner_left_sink, &er->cloner_queues.left); 361 - u_sink_queue_create(xfctx, 0, &er->cloner_right_sink, &er->cloner_queues.right); 360 + er->cloner_queues.cam_count = 2; 361 + u_sink_queue_create(xfctx, 0, &er->cloner_left_sink, &er->cloner_queues.cams[0]); 362 + u_sink_queue_create(xfctx, 0, &er->cloner_right_sink, &er->cloner_queues.cams[1]); 362 363 er->cloner_queues.imu = &er->cloner_imu_sink; 363 364 er->cloner_queues.gt = &er->cloner_gt_sink; 364 365 ··· 369 370 er->cloner_right_sink.push_frame = euroc_recorder_receive_right; 370 371 371 372 // Then, make a queue to save frame sinks to disk in a separate thread 372 - u_sink_queue_create(xfctx, 0, &er->writer_left_sink, &er->writer_queues.left); 373 - u_sink_queue_create(xfctx, 0, &er->writer_right_sink, &er->writer_queues.right); 373 + er->writer_queues.cam_count = 2; 374 + u_sink_queue_create(xfctx, 0, &er->writer_left_sink, &er->writer_queues.cams[0]); 375 + u_sink_queue_create(xfctx, 0, &er->writer_right_sink, &er->writer_queues.cams[1]); 374 376 er->writer_queues.imu = nullptr; 375 377 er->writer_queues.gt = nullptr; 376 378
+9 -6
src/xrt/auxiliary/tracking/t_tracker_slam.cpp
··· 1224 1224 t_slam_frame_sink_push_left(struct xrt_frame_sink *sink, struct xrt_frame *frame) 1225 1225 { 1226 1226 auto &t = *container_of(sink, TrackerSlam, left_sink); 1227 - push_frame(t, frame, true); 1227 + int cam_id = 0; 1228 + push_frame(t, frame, cam_id); 1228 1229 u_sink_debug_push_frame(&t.ui_left_sink, frame); 1229 - xrt_sink_push_frame(t.euroc_recorder->left, frame); 1230 + xrt_sink_push_frame(t.euroc_recorder->cams[0], frame); 1230 1231 } 1231 1232 1232 1233 extern "C" void 1233 1234 t_slam_frame_sink_push_right(struct xrt_frame_sink *sink, struct xrt_frame *frame) 1234 1235 { 1235 1236 auto &t = *container_of(sink, TrackerSlam, right_sink); 1236 - push_frame(t, frame, false); 1237 + int cam_id = 1; 1238 + push_frame(t, frame, cam_id); 1237 1239 u_sink_debug_push_frame(&t.ui_right_sink, frame); 1238 - xrt_sink_push_frame(t.euroc_recorder->right, frame); 1240 + xrt_sink_push_frame(t.euroc_recorder->cams[1], frame); 1239 1241 } 1240 1242 1241 1243 extern "C" void ··· 1372 1374 t.imu_sink.push_imu = t_slam_imu_sink_push; 1373 1375 t.gt_sink.push_pose = t_slam_gt_sink_push; 1374 1376 1375 - t.sinks.left = &t.left_sink; 1376 - t.sinks.right = &t.right_sink; 1377 + t.sinks.cam_count = NUM_CAMS; 1378 + t.sinks.cams[0] = &t.left_sink; 1379 + t.sinks.cams[1] = &t.right_sink; 1377 1380 t.sinks.imu = &t.imu_sink; 1378 1381 t.sinks.gt = &t.gt_sink; 1379 1382
+11 -11
src/xrt/drivers/euroc/euroc_player.cpp
··· 463 463 } 464 464 ep->img_seq++; 465 465 466 - xrt_sink_push_frame(ep->in_sinks.left, left_xf); 466 + xrt_sink_push_frame(ep->in_sinks.cams[0], left_xf); 467 467 if (stereo) { 468 - xrt_sink_push_frame(ep->in_sinks.right, right_xf); 468 + xrt_sink_push_frame(ep->in_sinks.cams[1], right_xf); 469 469 } 470 470 471 471 xrt_frame_reference(&left_xf, NULL); ··· 662 662 struct euroc_player *ep = container_of(sink, struct euroc_player, left_sink); 663 663 EUROC_TRACE(ep, "left img t=%ld source_t=%ld", xf->timestamp, xf->source_timestamp); 664 664 u_sink_debug_push_frame(&ep->ui_left_sink, xf); 665 - if (ep->out_sinks.left) { 666 - xrt_sink_push_frame(ep->out_sinks.left, xf); 665 + if (ep->out_sinks.cams[0]) { 666 + xrt_sink_push_frame(ep->out_sinks.cams[0], xf); 667 667 } 668 668 } 669 669 ··· 673 673 struct euroc_player *ep = container_of(sink, struct euroc_player, right_sink); 674 674 EUROC_TRACE(ep, "right img t=%ld source_t=%ld", xf->timestamp, xf->source_timestamp); 675 675 u_sink_debug_push_frame(&ep->ui_right_sink, xf); 676 - if (ep->out_sinks.right) { 677 - xrt_sink_push_frame(ep->out_sinks.right, xf); 676 + if (ep->out_sinks.cams[1]) { 677 + xrt_sink_push_frame(ep->out_sinks.cams[1], xf); 678 678 } 679 679 } 680 680 ··· 714 714 715 715 if (xs == NULL && capture_type == XRT_FS_CAPTURE_TYPE_TRACKING) { 716 716 EUROC_INFO(ep, "Starting Euroc Player in tracking mode"); 717 - if (ep->out_sinks.left == NULL) { 717 + if (ep->out_sinks.cams[0] == NULL) { 718 718 EUROC_WARN(ep, "No left sink provided, will keep running but tracking is unlikely to work"); 719 719 } 720 720 if (ep->playback.play_from_start) { ··· 722 722 } 723 723 } else if (xs != NULL && capture_type == XRT_FS_CAPTURE_TYPE_CALIBRATION) { 724 724 EUROC_INFO(ep, "Starting Euroc Player in calibration mode, will stream only left frames right away"); 725 - ep->out_sinks.left = xs; 725 + ep->out_sinks.cams[0] = xs; 726 726 euroc_player_start_btn_cb(ep); 727 727 } else { 728 728 EUROC_ASSERT(false, "Unsupported stream configuration xs=%p capture_type=%d", (void *)xs, capture_type); ··· 970 970 ep->left_sink.push_frame = receive_left_frame; 971 971 ep->right_sink.push_frame = receive_right_frame; 972 972 ep->imu_sink.push_imu = receive_imu_sample; 973 - ep->in_sinks.left = &ep->left_sink; 974 - ep->in_sinks.right = &ep->right_sink; 973 + ep->in_sinks.cam_count = 2; 974 + ep->in_sinks.cams[0] = &ep->left_sink; 975 + ep->in_sinks.cams[1] = &ep->right_sink; 975 976 ep->in_sinks.imu = &ep->imu_sink; 976 - ep->out_sinks = {0, 0, 0, 0}; 977 977 978 978 struct xrt_fs *xfs = &ep->base; 979 979 xfs->enumerate_modes = euroc_player_enumerate_modes;
+13 -11
src/xrt/drivers/realsense/rs_hdev.c
··· 638 638 xf_left->timestamp = ts; 639 639 xf_right->timestamp = ts; 640 640 641 - xrt_sink_push_frame(rs->in_sinks.left, xf_left); 642 - xrt_sink_push_frame(rs->in_sinks.right, xf_right); 641 + xrt_sink_push_frame(rs->in_sinks.cams[0], xf_left); 642 + xrt_sink_push_frame(rs->in_sinks.cams[1], xf_right); 643 643 } else { 644 644 // This usually happens only once at start and never again 645 645 RS_WARN(rs, "Realsense device sent left and right frames with different timestamps %ld != %ld", ··· 648 648 649 649 xrt_frame_reference(&xf_right, NULL); 650 650 } else { 651 - xrt_sink_push_frame(rs->in_sinks.left, xf_left); 651 + xrt_sink_push_frame(rs->in_sinks.cams[0], xf_left); 652 652 } 653 653 654 654 xrt_frame_reference(&xf_left, NULL); ··· 872 872 { 873 873 struct rs_source *rs = rs_source_from_xfs(xfs); 874 874 if (xs == NULL && capture_type == XRT_FS_CAPTURE_TYPE_TRACKING) { 875 - RS_ASSERT(rs->out_sinks.left != NULL, "No left sink provided"); 875 + RS_ASSERT(rs->out_sinks.cams[0] != NULL, "No left sink provided"); 876 876 RS_INFO(rs, "Starting RealSense stream in tracking mode"); 877 877 } else if (xs != NULL && capture_type == XRT_FS_CAPTURE_TYPE_CALIBRATION) { 878 878 RS_INFO(rs, "Starting RealSense stream in calibration mode, will stream only left frames"); 879 - rs->out_sinks.left = xs; 879 + rs->out_sinks.cam_count = 1; 880 + rs->out_sinks.cams[0] = xs; 880 881 } else { 881 882 RS_ASSERT(false, "Unsupported stream configuration xs=%p capture_type=%d", (void *)xs, capture_type); 882 883 return false; ··· 915 916 struct rs_source *rs = container_of(sink, struct rs_source, left_sink); 916 917 RS_TRACE(rs, "left img t=%ld source_t=%ld", xf->timestamp, xf->source_timestamp); 917 918 u_sink_debug_push_frame(&rs->ui_left_sink, xf); 918 - if (rs->out_sinks.left) { 919 - xrt_sink_push_frame(rs->out_sinks.left, xf); 919 + if (rs->out_sinks.cams[0]) { 920 + xrt_sink_push_frame(rs->out_sinks.cams[0], xf); 920 921 } 921 922 } 922 923 ··· 926 927 struct rs_source *rs = container_of(sink, struct rs_source, right_sink); 927 928 RS_TRACE(rs, "right img t=%ld source_t=%ld", xf->timestamp, xf->source_timestamp); 928 929 u_sink_debug_push_frame(&rs->ui_right_sink, xf); 929 - if (rs->out_sinks.right) { 930 - xrt_sink_push_frame(rs->out_sinks.right, xf); 930 + if (rs->out_sinks.cams[1]) { 931 + xrt_sink_push_frame(rs->out_sinks.cams[1], xf); 931 932 } 932 933 } 933 934 ··· 1096 1097 rs->left_sink.push_frame = receive_left_frame; 1097 1098 rs->right_sink.push_frame = receive_right_frame; 1098 1099 rs->imu_sink.push_imu = receive_imu_sample; 1099 - rs->in_sinks.left = &rs->left_sink; 1100 - rs->in_sinks.right = &rs->right_sink; 1100 + rs->in_sinks.cam_count = 2; 1101 + rs->in_sinks.cams[0] = &rs->left_sink; 1102 + rs->in_sinks.cams[1] = &rs->right_sink; 1101 1103 rs->in_sinks.imu = &rs->imu_sink; 1102 1104 1103 1105 // Prepare UI
+10 -12
src/xrt/drivers/rift_s/rift_s_tracker.c
··· 419 419 struct xrt_frame_sink *entry_left_sink = NULL; 420 420 struct xrt_frame_sink *entry_right_sink = NULL; 421 421 422 - u_sink_split_create(xfctx, slam_sinks->left, hand_sinks->left, &entry_left_sink); 423 - u_sink_split_create(xfctx, slam_sinks->right, hand_sinks->right, &entry_right_sink); 422 + u_sink_split_create(xfctx, slam_sinks->cams[0], hand_sinks->cams[0], &entry_left_sink); 423 + u_sink_split_create(xfctx, slam_sinks->cams[1], hand_sinks->cams[1], &entry_right_sink); 424 424 425 - entry_sinks = (struct xrt_slam_sinks){ 426 - .left = entry_left_sink, 427 - .right = entry_right_sink, 428 - .imu = slam_sinks->imu, 429 - .gt = slam_sinks->gt, 430 - }; 425 + entry_sinks = *slam_sinks; 426 + entry_sinks.cam_count = 2; 427 + entry_sinks.cams[0] = entry_left_sink; 428 + entry_sinks.cams[1] = entry_right_sink; 431 429 } else if (slam_enabled) { 432 430 entry_sinks = *slam_sinks; 433 431 } else if (hand_enabled) { ··· 595 593 t->last_frame_time = frame_time; 596 594 os_mutex_unlock(&t->mutex); 597 595 598 - if (t->slam_sinks.left) { 596 + if (t->slam_sinks.cams[0]) { 599 597 left_frame->timestamp = frame_time; 600 - xrt_sink_push_frame(t->slam_sinks.left, left_frame); 598 + xrt_sink_push_frame(t->slam_sinks.cams[0], left_frame); 601 599 } 602 600 603 - if (t->slam_sinks.right) { 601 + if (t->slam_sinks.cams[1]) { 604 602 right_frame->timestamp = frame_time; 605 - xrt_sink_push_frame(t->slam_sinks.right, right_frame); 603 + xrt_sink_push_frame(t->slam_sinks.cams[1], right_frame); 606 604 } 607 605 } 608 606
+6 -5
src/xrt/drivers/vive/vive_source.c
··· 128 128 129 129 VIVE_TRACE(vs, "sbs img t=%ld source_t=%ld", xf->timestamp, xf->source_timestamp); 130 130 131 - if (vs->out_sinks.left) { // The split into left right will happen downstream 132 - xrt_sink_push_frame(vs->out_sinks.left, xf); 131 + if (vs->out_sinks.cams[0]) { // The split into left right will happen downstream 132 + xrt_sink_push_frame(vs->out_sinks.cams[0], xf); 133 133 } 134 134 } 135 135 ··· 178 178 // Setup sinks 179 179 vs->sbs_sink.push_frame = vive_source_receive_sbs_frame; 180 180 vs->imu_sink.push_imu = vive_source_receive_imu_sample; 181 - vs->in_sinks.left = &vs->sbs_sink; 182 - vs->in_sinks.right = NULL; 181 + vs->in_sinks.cam_count = 1; 182 + vs->in_sinks.cams[0] = &vs->sbs_sink; 183 183 vs->in_sinks.imu = &vs->imu_sink; 184 184 185 185 vs->timestamps_have_been_zero_until_now = true; ··· 219 219 vive_source_hook_into_sinks(struct vive_source *vs, struct xrt_slam_sinks *sinks) 220 220 { 221 221 vs->out_sinks = *sinks; 222 - sinks->left = vs->in_sinks.left; 222 + sinks->cam_count = 1; 223 + sinks->cams[0] = vs->in_sinks.cams[0]; 223 224 }
+6 -8
src/xrt/drivers/wmr/wmr_hmd.c
··· 1708 1708 struct xrt_frame_sink *entry_left_sink = NULL; 1709 1709 struct xrt_frame_sink *entry_right_sink = NULL; 1710 1710 1711 - u_sink_split_create(&wh->tracking.xfctx, slam_sinks->left, hand_sinks->left, &entry_left_sink); 1712 - u_sink_split_create(&wh->tracking.xfctx, slam_sinks->right, hand_sinks->right, &entry_right_sink); 1711 + u_sink_split_create(&wh->tracking.xfctx, slam_sinks->cams[0], hand_sinks->cams[0], &entry_left_sink); 1712 + u_sink_split_create(&wh->tracking.xfctx, slam_sinks->cams[1], hand_sinks->cams[1], &entry_right_sink); 1713 1713 1714 - entry_sinks = (struct xrt_slam_sinks){ 1715 - .left = entry_left_sink, 1716 - .right = entry_right_sink, 1717 - .imu = slam_sinks->imu, 1718 - .gt = slam_sinks->gt, 1719 - }; 1714 + entry_sinks = *slam_sinks; 1715 + entry_sinks.cam_count = 2; 1716 + entry_sinks.cams[0] = entry_left_sink; 1717 + entry_sinks.cams[1] = entry_right_sink; 1720 1718 } else if (slam_enabled) { 1721 1719 entry_sinks = *slam_sinks; 1722 1720 } else if (hand_enabled) {
+11 -8
src/xrt/drivers/wmr/wmr_source.c
··· 93 93 xf->timestamp += ws->cam_hw2mono; 94 94 WMR_TRACE(ws, "left img t=%ld source_t=%ld", xf->timestamp, xf->source_timestamp); 95 95 u_sink_debug_push_frame(&ws->ui_left_sink, xf); 96 - if (ws->out_sinks.left && ws->first_imu_received) { 97 - xrt_sink_push_frame(ws->out_sinks.left, xf); 96 + if (ws->out_sinks.cams[0] && ws->first_imu_received) { 97 + xrt_sink_push_frame(ws->out_sinks.cams[0], xf); 98 98 } 99 99 } 100 100 ··· 105 105 xf->timestamp += ws->cam_hw2mono; 106 106 WMR_TRACE(ws, "right img t=%ld source_t=%ld", xf->timestamp, xf->source_timestamp); 107 107 u_sink_debug_push_frame(&ws->ui_right_sink, xf); 108 - if (ws->out_sinks.right && ws->first_imu_received) { 109 - xrt_sink_push_frame(ws->out_sinks.right, xf); 108 + if (ws->out_sinks.cams[1] && ws->first_imu_received) { 109 + xrt_sink_push_frame(ws->out_sinks.cams[1], xf); 110 110 } 111 111 } 112 112 ··· 206 206 WMR_INFO(ws, "Starting WMR stream in tracking mode"); 207 207 } else if (xs != NULL && capture_type == XRT_FS_CAPTURE_TYPE_CALIBRATION) { 208 208 WMR_INFO(ws, "Starting WMR stream in calibration mode, will stream only left frames"); 209 - ws->out_sinks.left = xs; 209 + ws->out_sinks.cam_count = 1; 210 + ws->out_sinks.cams[0] = xs; 210 211 } else { 211 212 WMR_ASSERT(false, "Unsupported stream configuration xs=%p capture_type=%d", (void *)xs, capture_type); 212 213 return false; ··· 302 303 ws->left_sink.push_frame = receive_left_frame; 303 304 ws->right_sink.push_frame = receive_right_frame; 304 305 ws->imu_sink.push_imu = receive_imu_sample; 305 - ws->in_sinks.left = &ws->left_sink; 306 - ws->in_sinks.right = &ws->right_sink; 306 + ws->in_sinks.cam_count = 2; 307 + ws->in_sinks.cams[0] = &ws->left_sink; 308 + ws->in_sinks.cams[1] = &ws->right_sink; 307 309 ws->in_sinks.imu = &ws->imu_sink; 308 - ws->camera = wmr_camera_open(dev_holo, ws->in_sinks.left, ws->in_sinks.right, cfg.n_cameras, ws->log_level); 310 + ws->camera = 311 + wmr_camera_open(dev_holo, ws->in_sinks.cams[0], ws->in_sinks.cams[1], cfg.n_cameras, ws->log_level); 309 312 ws->config = cfg; 310 313 311 314 // Setup UI
+3 -2
src/xrt/include/xrt/xrt_tracking.h
··· 10 10 #pragma once 11 11 12 12 #define XRT_TRACKING_NAME_LEN 256 13 + #define XRT_TRACKING_MAX_SLAM_CAMS 5 13 14 14 15 #include "xrt/xrt_defines.h" 15 16 ··· 173 174 */ 174 175 struct xrt_slam_sinks 175 176 { 176 - struct xrt_frame_sink *left; 177 - struct xrt_frame_sink *right; 177 + int cam_count; 178 + struct xrt_frame_sink *cams[XRT_TRACKING_MAX_SLAM_CAMS]; 178 179 struct xrt_imu_sink *imu; 179 180 struct xrt_pose_sink *gt; //!< Can receive ground truth poses if available 180 181 };
+2 -1
src/xrt/state_trackers/gui/gui_scene_calibrate.c
··· 354 354 if (cs->settings->camera_type == XRT_SETTINGS_CAMERA_TYPE_SLAM) { 355 355 struct xrt_frame_sink *tmp = cali; 356 356 struct xrt_slam_sinks sinks; 357 - u_sink_combiner_create(cs->xfctx, tmp, &sinks.left, &sinks.right); 357 + sinks.cam_count = 2; 358 + u_sink_combiner_create(cs->xfctx, tmp, &sinks.cams[0], &sinks.cams[1]); 358 359 359 360 xrt_fs_slam_stream_start(cs->xfs, &sinks); 360 361 } else {
+9 -9
src/xrt/targets/common/target_builder_lighthouse.c
··· 416 416 struct xrt_frame_sink *entry_sbs_sink = NULL; 417 417 418 418 if (slam_enabled && hand_enabled) { 419 - u_sink_split_create(&lhs->devices->xfctx, slam_sinks->left, hand_sinks->left, &entry_left_sink); 420 - u_sink_split_create(&lhs->devices->xfctx, slam_sinks->right, hand_sinks->right, &entry_right_sink); 419 + u_sink_split_create(&lhs->devices->xfctx, slam_sinks->cams[0], hand_sinks->cams[0], &entry_left_sink); 420 + u_sink_split_create(&lhs->devices->xfctx, slam_sinks->cams[1], hand_sinks->cams[1], &entry_right_sink); 421 421 u_sink_stereo_sbs_to_slam_sbs_create(&lhs->devices->xfctx, entry_left_sink, entry_right_sink, 422 422 &entry_sbs_sink); 423 423 u_sink_create_format_converter(&lhs->devices->xfctx, XRT_FORMAT_L8, entry_sbs_sink, &entry_sbs_sink); 424 424 } else if (slam_enabled) { 425 - entry_left_sink = slam_sinks->left; 426 - entry_right_sink = slam_sinks->right; 425 + entry_left_sink = slam_sinks->cams[0]; 426 + entry_right_sink = slam_sinks->cams[1]; 427 427 u_sink_stereo_sbs_to_slam_sbs_create(&lhs->devices->xfctx, entry_left_sink, entry_right_sink, 428 428 &entry_sbs_sink); 429 429 u_sink_create_format_converter(&lhs->devices->xfctx, XRT_FORMAT_L8, entry_sbs_sink, &entry_sbs_sink); 430 430 } else if (hand_enabled) { 431 - entry_left_sink = hand_sinks->left; 432 - entry_right_sink = hand_sinks->right; 431 + entry_left_sink = hand_sinks->cams[0]; 432 + entry_right_sink = hand_sinks->cams[1]; 433 433 u_sink_stereo_sbs_to_slam_sbs_create(&lhs->devices->xfctx, entry_left_sink, entry_right_sink, 434 434 &entry_sbs_sink); 435 435 u_sink_create_format_converter(&lhs->devices->xfctx, XRT_FORMAT_L8, entry_sbs_sink, &entry_sbs_sink); ··· 441 441 u_sink_simple_queue_create(&lhs->devices->xfctx, entry_sbs_sink, &entry_sbs_sink); 442 442 443 443 struct xrt_slam_sinks entry_sinks = { 444 - .left = entry_sbs_sink, 445 - .right = NULL, // v4l2 streams a single SBS frame so we ignore the right sink 444 + .cam_count = 1, 445 + .cams = {entry_sbs_sink}, 446 446 .imu = slam_enabled ? slam_sinks->imu : NULL, 447 447 .gt = slam_enabled ? slam_sinks->gt : NULL, 448 448 }; ··· 479 479 vive_source_hook_into_sinks(vs, &sinks); 480 480 } 481 481 482 - success = xrt_fs_stream_start(lhs->xfs, sinks.left, XRT_FS_CAPTURE_TYPE_TRACKING, mode); 482 + success = xrt_fs_stream_start(lhs->xfs, sinks.cams[0], XRT_FS_CAPTURE_TYPE_TRACKING, mode); 483 483 484 484 if (!success) { 485 485 LH_ERROR("Unable to start data streaming");
+3 -2
src/xrt/tracking/hand/t_hand_tracking_async.c
··· 233 233 struct ht_async_impl *hta = U_TYPED_CALLOC(struct ht_async_impl); 234 234 hta->base.left.push_frame = ht_async_receive_left; 235 235 hta->base.right.push_frame = ht_async_receive_right; 236 - hta->base.sinks.left = &hta->base.left; 237 - hta->base.sinks.right = &hta->base.right; 236 + hta->base.sinks.cam_count = 2; 237 + hta->base.sinks.cams[0] = &hta->base.left; 238 + hta->base.sinks.cams[1] = &hta->base.right; 238 239 hta->base.node.break_apart = ht_async_break_apart; 239 240 hta->base.node.destroy = ht_async_destroy; 240 241 hta->base.get_hand = ht_async_get_hand;