Dropped frames in video recording

Hi everyone,
I have a Logitech Webcam C270 supporting YUYV 1280x720@10 FPS and Motion-JPEG 1280x720@30 FPS according to the output from “v4l2-ctl --list-formats-ext”.

I am trying to record 1280x720@30 FPS with the code below; however, the recorded video length is ~ 20% to 50% shorter than the real-time. My explanation is Python is not fast enough to capture all the frames, but would like someone with more experience to confirm this.
(The workstation is a Ubuntu 22.04.3, 16-core CPU, 32G of RAM. )

Code:

frame_width = 1280
frame_height = 720
frame_rate = 30 # I have changed this from 15 to 30, with the results below.
capture_duration = 10

cap = cv2.VideoCapture(0, cv2.CAP_V4L2)

if (cap.isOpened() == False):
print(“Unable to access camera”)

cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(‘M’,‘J’,‘P’,‘G’))
cap.set(cv2.CAP_PROP_FRAME_WIDTH, frame_width)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, frame_height)
cap.set(cv2.CAP_PROP_FPS, frame_rate)
time.sleep(1)

print(f"Width: {cap.get(cv2.CAP_PROP_FRAME_WIDTH)}“)
print(f"Height: {cap.get(cv2.CAP_PROP_FRAME_HEIGHT)}”)
print(f"FPS: {cap.get(cv2.CAP_PROP_FPS)}")

fileName = f"out{frame_width}x{frame_height}@{frame_rate}.avi"
print(f"Saving to: {fileName}")

out = cv2.VideoWriter(fileName,
cv2.VideoWriter_fourcc(‘M’,‘J’,‘P’,‘G’),
frame_rate,
(frame_width,frame_height))

start_time = time.time()
while( int(time.time() - start_time) <= capture_duration ):
ret, frame = cap.read()

if not ret:
break

frame = cv2.putText(frame, str(datetime.datetime.now()),
(50, 50),cv2.FONT_HERSHEY_SIMPLEX, 1,
(255, 255, 255), 2, cv2.LINE_AA)

out.write(frame)

cap.release()
out.release()

Results:

$ ffprobe out1280x720@15.avi 2>&1 | grep “Duration|Stream”
Duration: 00:00:10.67, start: 0.000000, bitrate: 8904 kb/s
Stream #0:0: Video: mjpeg (Baseline) (MJPG / 0x47504A4D), yuvj420p(pc, bt470bg/unknown/unknown), 1280x720, 8952 kb/s, 15 fps, 15 tbr, 15 tbn

$ ffprobe out1280x720@20.avi 2>&1 | grep “Duration|Stream”
Duration: 00:00:08.05, start: 0.000000, bitrate: 12437 kb/s
Stream #0:0: Video: mjpeg (Baseline) (MJPG / 0x47504A4D), yuvj420p(pc, bt470bg/unknown/unknown), 1280x720, 12501 kb/s, 20 fps, 20 tbr, 20 tbn

$ ffprobe out1280x720@30.avi 2>&1 | grep “Duration|Stream”
Duration: 00:00:05.37, start: 0.000000, bitrate: 18467 kb/s
Stream #0:0: Video: mjpeg (Baseline) (MJPG / 0x47504A4D), yuvj420p(pc, bt470bg/unknown/unknown), 1280x720, 18556 kb/s, 30 fps, 30 tbr, 30 tbn

It seems ffmpeg can record from this camera as:
$ ffmpeg -t 10 -f video4linux2 -input_format mjpeg -framerate 30 -video_size 1280x720 -i /dev/video0 out.avi

$ ffprobe out.avi 2>&1 | grep “Duration|Stream”
Duration: 00:00:10.00, start: 0.000000, bitrate: 434 kb/s
Stream #0:0: Video: mpeg4 (Simple Profile) (FMP4 / 0x34504D46), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 425 kb/s, 30 fps, 30 tbr, 30 tbn, 30 tbc

Thanks for your time.