下载地址:http://m.pan38.com/download.php?code=BWQJMR 访问密码(可选):8876
完整的虚拟摄像头实现方案,包含多个模块的代码实现。这个项目可以模拟摄像头设备,并在微信/QQ视频聊天时替换为自定义视频源。
import cv2
import numpy as np
import pyfakewebcam
import threading
import time
from queue import Queue
class VirtualCamera:
def init(self, width=640, height=480, fps=30, device="/dev/video20"):
self.width = width
self.height = height
self.fps = fps
self.device = device
self.frame_queue = Queue(maxsize=10)
self.running = False
self.camera = pyfakewebcam.FakeWebcam(device, width, height)
def start(self):
self.running = True
self.thread = threading.Thread(target=self._update_frame)
self.thread.daemon = True
self.thread.start()
def stop(self):
self.running = False
if hasattr(self, 'thread'):
self.thread.join()
def _update_frame(self):
last_time = time.time()
interval = 1.0 / self.fps
while self.running:
current_time = time.time()
elapsed = current_time - last_time
if elapsed < interval:
time.sleep(interval - elapsed)
continue
last_time = current_time
if not self.frame_queue.empty():
frame = self.frame_queue.get()
self.camera.schedule_frame(frame)
def feed_frame(self, frame):
if frame.shape[:2] != (self.height, self.width):
frame = cv2.resize(frame, (self.width, self.height))
if frame.dtype != np.uint8:
frame = (frame * 255).astype(np.uint8)
if len(frame.shape) == 2:
frame = cv2.cvtColor(frame, cv2.COLOR_GRAY2BGR)
elif frame.shape[2] == 4:
frame = frame[:, :, :3]
if self.frame_queue.full():
self.frame_queue.get()
self.frame_queue.put(frame)
import cv2
import numpy as np
from enum import Enum
class VideoSourceType(Enum):
IMAGE = 1
VIDEO = 2
SCREEN = 3
WEBCAM = 4
ANIMATION = 5
class VideoSource:
def init(self, source_type, source_path=None):
self.source_type = source_type
self.source_path = source_path
self.cap = None
self.current_frame = None
self._init_source()
def _init_source(self):
if self.source_type == VideoSourceType.IMAGE:
self.current_frame = cv2.imread(self.source_path)
if self.current_frame is None:
raise ValueError(f"无法加载图片: {self.source_path}")
elif self.source_type == VideoSourceType.VIDEO:
self.cap = cv2.VideoCapture(self.source_path)
if not self.cap.isOpened():
raise ValueError(f"无法打开视频文件: {self.source_path}")
elif self.source_type == VideoSourceType.WEBCAM:
self.cap = cv2.VideoCapture(0)
if not self.cap.isOpened():
raise ValueError("无法打开摄像头")
elif self.source_type == VideoSourceType.SCREEN:
try:
import mss
self.sct = mss.mss()
except ImportError:
raise ImportError("请先安装mss库: pip install mss")
elif self.source_type == VideoSourceType.ANIMATION:
self.animation_params = {
'bg_color': (0, 0, 0),
'text': "虚拟摄像头",
'text_color': (255, 255, 255),
'text_pos': (50, 50),
'text_size': 1,
'text_thickness': 2,
'animation_type': 'scroll'
}
def get_frame(self):
if self.source_type == VideoSourceType.IMAGE:
return self.current_frame.copy()
elif self.source_type in [VideoSourceType.VIDEO, VideoSourceType.WEBCAM]:
ret, frame = self.cap.read()
if not ret:
if self.source_type == VideoSourceType.VIDEO:
self.cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
ret, frame = self.cap.read()
if not ret:
raise RuntimeError("无法读取视频帧")
else:
raise RuntimeError("无法从摄像头获取帧")
return frame
elif self.source_type == VideoSourceType.SCREEN:
monitor = self.sct.monitors[1]
sct_img = self.sct.grab(monitor)
frame = np.array(sct_img)
frame = cv2.cvtColor(frame, cv2.COLOR_BGRA2BGR)
return frame
elif self.source_type == VideoSourceType.ANIMATION:
frame = np.zeros((480, 640, 3), dtype=np.uint8)
frame[:] = self.animation_params['bg_color']
if self.animation_params['animation_type'] == 'scroll':
x = int(time.time() * 50) % 640
cv2.putText(frame, self.animation_params['text'],
(x, self.animation_params['text_pos'][1]),
cv2.FONT_HERSHEY_SIMPLEX,
self.animation_params['text_size'],
self.animation_params['text_color'],
self.animation_params['text_thickness'])
else:
cv2.putText(frame, self.animation_params['text'],
self.animation_params['text_pos'],
cv2.FONT_HERSHEY_SIMPLEX,
self.animation_params['text_size'],
self.animation_params['text_color'],
self.animation_params['text_thickness'])
return frame
def release(self):
if self.cap is not None:
self.cap.release()
import argparse
import time
from virtual_camera import VirtualCamera
from video_source import VideoSource, VideoSourceType
def parse_args():
parser = argparse.ArgumentParser(description="虚拟摄像头程序")
parser.add_argument("--source", type=str, required=True,
help="视频源类型: image, video, webcam, screen, animation")
parser.add_argument("--path", type=str, default=None,
help="图片/视频文件路径(当source为image或video时)")
parser.add_argument("--device", type=str, default="/dev/video20",
help="虚拟摄像头设备路径")
parser.add_argument("--width", type=int, default=640,
help="视频宽度")
parser.add_argument("--height", type=int, default=480,
help="视频高度")
parser.add_argument("--fps", type=int, default=30,
help="帧率")
return parser.parse_args()
def main():
args = parse_args()
# 映射参数到视频源类型
source_map = {
'image': VideoSourceType.IMAGE,
'video': VideoSourceType.VIDEO,
'webcam': VideoSourceType.WEBCAM,
'screen': VideoSourceType.SCREEN,
'animation': VideoSourceType.ANIMATION
}
source_type = source_map.get(args.source.lower())
if source_type is None:
raise ValueError(f"不支持的视频源类型: {args.source}")
# 初始化视频源
try:
video_source = VideoSource(source_type, args.path)
except Exception as e:
print(f"初始化视频源失败: {str(e)}")
return
# 初始化虚拟摄像头
try:
vcam = VirtualCamera(
width=args.width,
height=args.height,
fps=args.fps,
device=args.device
)
vcam.start()
print(f"虚拟摄像头已启动,设备路径: {args.device}")
# 主循环
try:
while True:
frame = video_source.get_frame()
vcam.feed_frame(frame)
time.sleep(1.0 / args.fps)
except KeyboardInterrupt:
print("接收到中断信号,正在停止...")
except Exception as e:
print(f"虚拟摄像头初始化失败: {str(e)}")
finally:
vcam.stop()
video_source.release()
print("程序已退出")
if name == "main":
main()
!/bin/bash
安装依赖
echo "正在安装系统依赖..."
sudo apt-get update
sudo apt-get install -y v4l2loopback-dkms v4l2loopback-utils ffmpeg python3-pip
创建虚拟摄像头设备
echo "正在创建虚拟摄像头设备..."
sudo modprobe v4l2loopback devices=1 video_nr=20 card_label="VirtualCam" exclusive_caps=1
安装Python依赖
echo "正在安装Python依赖..."
pip3 install opencv-python numpy pyfakewebcam mss
设置udev规则(可选)
echo "设置udev规则..."
cat < /dev/null
KERNEL=="video*", ATTR{name}=="VirtualCam", SYMLINK+="virtual-camera"
EOF
sudo udevadm control --reload-rules
sudo udevadm trigger
echo "安装完成!"
echo "使用方法: python3 main.py --source [--path ]"
这个项目包含4个主要模块:虚拟摄像头核心模块、视频源管理模块、主控制程序和安装脚本。使用时需要先运行install.sh安装依赖,然后通过main.py启动程序。代码支持多种视频源输入,包括图片、视频文件、真实摄像头、屏幕捕获和动画效果。