Skip to content

Instantly share code, notes, and snippets.

@colindean
Created January 25, 2026 20:53
Show Gist options
  • Select an option

  • Save colindean/10c5e9d9497103494f7b19f741abb4a6 to your computer and use it in GitHub Desktop.

Select an option

Save colindean/10c5e9d9497103494f7b19f741abb4a6 to your computer and use it in GitHub Desktop.
Recording from two webcams simulatenously for poor man's VR

Recording from two webcams simulatenously for poor man's VR

I think netthinktank wrote this originally and I made some small modifications to it.

It worked enough for me to prove that I couldn't get my pair of Logitech BRIO webcams arranged in a way to make real VR recording happen.

import cv2
import time
import logging
logging.basicConfig(level=logging.DEBUG, format="%(asctime)s %(levelname)s %(message)s")
logger = logging.getLogger(__name__)
def main():
logger.info("Initialize cameras with higher priority for VR")
cap_left = cv2.VideoCapture(0, cv2.CAP_ANY)
cap_right = cv2.VideoCapture(1, cv2.CAP_ANY)
logger.info("Set desired frame rate and resolution")
fps = 30 # Higher FPS gives smoother VR experience
frame_width = 3840 // 2 # Split 4K into 1920 per eye
frame_height = 2160
logger.debug(f"{frame_width=}x{frame_height=} @ {fps=}")
logger.info("Configure cameras...")
cap_left.set(cv2.CAP_PROP_FRAME_WIDTH, 3840)
cap_left.set(cv2.CAP_PROP_FRAME_HEIGHT, 2160)
cap_left.set(cv2.CAP_PROP_FPS, fps)
cap_right.set(cv2.CAP_PROP_FRAME_WIDTH, 3840)
cap_right.set(cv2.CAP_PROP_FRAME_HEIGHT, 2160)
cap_right.set(cv2.CAP_PROP_FPS, fps)
logger.info("Verify settings...")
if not cap_left.isOpened() or not cap_right.isOpened():
raise RuntimeError("Could not open video cameras")
# Get actual resolutions (might differ from requested)
width_left = int(cap_left.get(cv2.CAP_PROP_FRAME_WIDTH))
height_left = int(cap_left.get(cv2.CAP_PROP_FRAME_HEIGHT))
width_right = int(cap_right.get(cv2.CAP_PROP_FRAME_WIDTH))
height_right = int(cap_right.get(cv2.CAP_PROP_FRAME_HEIGHT))
logger.info(f"Using left camera: {width_left}x{height_left} at {fps} FPS")
logger.info(f"Using right camera: {width_right}x{height_right} at {fps} FPS")
logger.info("Set up video writer with H264 codec for better compression")
output_filename = "vr_stream.mp4"
logger.info("Writing to %s", output_filename)
fourcc = cv2.VideoWriter_fourcc(*'H264') # Better compression than MJPG
out = cv2.VideoWriter(output_filename, fourcc, fps, (width_left + width_right, height_left))
logger.info("Warmup - let cameras stabilize")
time.sleep(2)
logger.info("VR streaming in progress. Press 'q' to quit...")
# Add frame counter for performance tracking
frame_count = 0
start_time = time.time()
while True:
# Read frames (non-blocking)
cap_left.grab()
cap_right.grab()
# Retrieve frames
ret_left, frame_left = cap_left.retrieve()
ret_right, frame_right = cap_right.retrieve()
if not ret_left or not ret_right:
logger.info("Frame capture failed. Attempting to reconnect...")
cap_left.release()
cap_right.release()
time.sleep(1)
cap_left = cv2.VideoCapture(0, cv2.CAP_V4L2)
cap_right = cv2.VideoCapture(1, cv2.CAP_V4L2)
continue
# Convert to RGB for proper color space (OpenCV uses BGR by default)
frame_left = cv2.cvtColor(frame_left, cv2.COLOR_BGR2RGB)
frame_right = cv2.cvtColor(frame_right, cv2.COLOR_BGR2RGB)
logger.debug("Stitching frame {frame_count+1}")
vr_frame = cv2.hconcat([frame_left, frame_right])
# Convert back to BGR for OpenCV operations
vr_frame = cv2.cvtColor(vr_frame, cv2.COLOR_RGB2BGR)
# Write to file
out.write(vr_frame)
# Display preview
cv2.imshow("VR Stream (Press q to quit)", vr_frame)
# Update frame counter
frame_count += 1
# Calculate and show FPS
if frame_count % 10 == 0:
elapsed = time.time() - start_time
current_fps = frame_count / elapsed
logger.info(f"Current FPS: {current_fps:.2f}")
# Check for exit
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# Cleanup
cap_left.release()
cap_right.release()
out.release()
cv2.destroyAllWindows()
# Calculate final performance metrics
total_time = time.time() - start_time
avg_fps = frame_count / total_time if frame_count > 0 else 0
logger.info(f"Session complete. Average FPS: {avg_fps:.2f}")
if __name__ == "__main__":
try:
main()
except Exception as e:
logger.error(f"Error: {str(e)}")
logger.error("Press any key to exit...")
cv2.waitKey(0)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment