Unable to set frame rate
tyler-rt opened this issue · 3 comments
Overview
When using a webcam, pyav fails to set frame rate.
Expected behavior
Frame rate should be changeable, as in this ffmpeg script which on my computer works at 60fps:
Actual behavior
Frame rate is not set. cc #1005
Investigation
I made a script with ffmpeg showing that I can change the FPS for my webcam from the default of 30fps to say 60fps or 15fps:
import cv2
import subprocess
import numpy as np
import time
# FFmpeg command
ffmpeg_command = [
'ffmpeg',
'-f', 'v4l2',
'-framerate', '60',
'-video_size', '1920x1080',
'-i', '/dev/video4',
'-f', 'rawvideo',
'-pix_fmt', 'rgb24',
'pipe:1'
]
# Start the FFmpeg process and suppress its output
process = subprocess.Popen(ffmpeg_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# Set up OpenCV window
cv2.namedWindow('Webcam', cv2.WINDOW_NORMAL)
cv2.resizeWindow('Webcam', 1920, 1080)
frame_count = 0
start_time = time.time()
while True:
# Read raw video frame from stdout
raw_frame = process.stdout.read(1920 * 1080 * 3)
if len(raw_frame) != 1920 * 1080 * 3:
break
# Convert raw frame to numpy array
frame = np.frombuffer(raw_frame, np.uint8).reshape((1080, 1920, 3))
# Display the frame using OpenCV
cv2.imshow('Webcam', frame)
frame_count += 1
# Calculate and print FPS every second
elapsed_time = time.time() - start_time
if elapsed_time >= 1.0:
fps = frame_count / elapsed_time
print(f"Average FPS: {fps:.2f}")
frame_count = 0
start_time = time.time()
# Exit on ESC key
if cv2.waitKey(1) & 0xFF == 27:
break
# Clean up
process.stdout.close()
process.wait()
process.terminate()
cv2.destroyAllWindows()
Reproduction
PyAV is unable to change frame rate:
import av
import cv2
import time
from fractions import Fraction
def open_usb_webcam(max_devices=10):
"Open VideoCapture with the last valid device (e.g. USB webcam) on the system."
for device_index in range(max_devices, 0, -1):
try:
input_device = f"/dev/video{device_index}"
# container = av.open(input_device)
container = av.open(input_device)
stream = container.streams.video[0]
return container, stream
except (av.AVError, IndexError):
continue
raise ValueError("No valid video capture devices found.")
# Open the webcam
container = av.open("/dev/video0")
stream = container.streams.video[0]
# Configure the stream for the specified settings
# Tested that we can change the stream width and height
stream.width = 1920
stream.height = 1080
# stream.width = 800
# stream.height = 600
# pix_fmt doesn't appear to matter for setting frame rate
stream.pix_fmt = 'yuv422p'
# however, we are unable to set the frame rate
stream.rate = Fraction(1,60)
# stream.framerate = Fraction(1,15)
# stream.rate = "15"
# Create OpenCV window
cv2.namedWindow('Webcam', cv2.WINDOW_NORMAL)
cv2.resizeWindow('Webcam', 1920, 1080)
# Start time
start_time = time.time()
frame_count = 0
duration = 3
for packet in container.demux(stream):
current_time = time.time()
if current_time - start_time >= duration:
break
for frame in packet.decode():
# Convert frame to OpenCV format (BGR)
img = frame.to_ndarray(format='bgr24')
# # Display the frame
# commenting out display has no impact on fps
cv2.imshow('Webcam', img)
frame_count += 1
if cv2.waitKey(1) & 0xFF == ord('q'):
break
pass
if current_time - start_time >= duration:
break
# Calculate and print the average FPS
elapsed_time = time.time() - start_time
average_fps = frame_count / elapsed_time
print(f"Average FPS: {average_fps:.2f}") # 30.00 FPS ???
print(img.shape)
# Release resources
cv2.destroyAllWindows()
container.close()
Versions
- OS: Ubuntu 22.04
- PyAV runtime:
PyAV v12.0.0
library configuration: --prefix=/home/reflex/miniconda3/envs/reflex --cc=/home/conda/feedstock_root/build_artifacts/ffmpeg_1716145014501/_build_env/bin/x86_64-conda-linux-gnu-cc --cxx=/home/conda/feedstock_root/build_artifacts/ffmpeg_1716145014501/_build_env/bin/x86_64-conda-linux-gnu-c++ --nm=/home/conda/feedstock_root/build_artifacts/ffmpeg_1716145014501/_build_env/bin/x86_64-conda-linux-gnu-nm --ar=/home/conda/feedstock_root/build_artifacts/ffmpeg_1716145014501/_build_env/bin/x86_64-conda-linux-gnu-ar --disable-doc --disable-openssl --enable-demuxer=dash --enable-hardcoded-tables --enable-libfreetype --enable-libharfbuzz --enable-libfontconfig --enable-libopenh264 --enable-libdav1d --enable-gnutls --enable-libmp3lame --enable-libvpx --enable-libass --enable-pthreads --enable-vaapi --enable-libopenvino --enable-gpl --enable-libx264 --enable-libx265 --enable-libaom --enable-libsvtav1 --enable-libxml2 --enable-pic --enable-shared --disable-static --enable-version3 --enable-zlib --enable-libopus --pkg-config=/home/conda/feedstock_root/build_artifacts/ffmpeg_1716145014501/_build_env/bin/pkg-config
library license: GPL version 3 or later
libavcodec 60. 31.102
libavdevice 60. 3.100
libavfilter 9. 12.100
libavformat 60. 16.100
libavutil 58. 29.100
libswresample 4. 12.100
libswscale 7. 5.100
Research
I have done the following:
- Checked the PyAV documentation
- Searched on Google
- Searched on Stack Overflow
- Looked through old GitHub issues
Additional context
stream.rate = Fraction(1,60)
is deprecated, and is removed in 12.1.0, released today.
anyway, you shouldn't be able to set the stream frame rate? (How would you specify variable frame rates?)
You're looking for either:
- Changing the device framerate
- Changing output's framerate by modifying each
VideoFrame
's pts
@WyattBlue indeed I want to set the device frame rate of the webcam so it acquires at 15 or 60 fps. Is this supported by PyAV currently? In ffmpeg
this is done with -framerate 60