297 lines
9.4 KiB
Python
297 lines
9.4 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
屏幕录制工具
|
||
支持录制当前屏幕并保存为视频文件
|
||
"""
|
||
|
||
import cv2
|
||
import numpy as np
|
||
import pyautogui
|
||
import threading
|
||
import time
|
||
from datetime import datetime
|
||
import os
|
||
|
||
class ScreenRecorder:
|
||
def __init__(self, output_dir="recordings", fps=20, quality=80, region=None):
|
||
"""
|
||
初始化屏幕录制器
|
||
|
||
Args:
|
||
output_dir (str): 输出目录
|
||
fps (int): 帧率
|
||
quality (int): 视频质量 (1-100)
|
||
region (tuple): 录制区域 (x, y, width, height),None表示全屏录制
|
||
"""
|
||
self.output_dir = output_dir
|
||
self.fps = fps
|
||
self.quality = quality
|
||
self.recording = False
|
||
self.paused = False
|
||
self.video_writer = None
|
||
self.thread = None
|
||
self.region = region
|
||
|
||
# 创建输出目录
|
||
if not os.path.exists(output_dir):
|
||
os.makedirs(output_dir)
|
||
|
||
# 获取屏幕尺寸
|
||
self.screen_size = pyautogui.size()
|
||
print(f"屏幕尺寸: {self.screen_size}")
|
||
|
||
# 设置录制区域
|
||
if self.region:
|
||
x, y, width, height = self.region
|
||
# 确保区域在屏幕范围内
|
||
x = max(0, min(x, self.screen_size[0] - 1))
|
||
y = max(0, min(y, self.screen_size[1] - 1))
|
||
width = min(width, self.screen_size[0] - x)
|
||
height = min(height, self.screen_size[1] - y)
|
||
self.region = (x, y, width, height)
|
||
self.record_size = (width, height)
|
||
print(f"录制区域: {self.region}")
|
||
else:
|
||
self.record_size = self.screen_size
|
||
print("录制模式: 全屏录制")
|
||
|
||
def start_recording(self, filename=None):
|
||
"""
|
||
开始录制
|
||
|
||
Args:
|
||
filename (str): 输出文件名,如果为None则自动生成
|
||
"""
|
||
if self.recording:
|
||
print("录制已在进行中")
|
||
return
|
||
|
||
if filename is None:
|
||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||
filename = f"screen_record_{timestamp}.mp4"
|
||
|
||
self.output_path = os.path.join(self.output_dir, filename)
|
||
|
||
# 设置视频编码器
|
||
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
|
||
self.video_writer = cv2.VideoWriter(
|
||
self.output_path,
|
||
fourcc,
|
||
self.fps,
|
||
self.record_size
|
||
)
|
||
|
||
self.recording = True
|
||
self.paused = False
|
||
|
||
# 在新线程中开始录制
|
||
self.thread = threading.Thread(target=self._record_loop)
|
||
self.thread.daemon = True
|
||
self.thread.start()
|
||
|
||
print(f"开始录制: {self.output_path}")
|
||
|
||
def _record_loop(self):
|
||
"""
|
||
录制循环
|
||
"""
|
||
while self.recording:
|
||
if not self.paused:
|
||
# 截取屏幕
|
||
if self.region:
|
||
# 区域录制
|
||
x, y, width, height = self.region
|
||
screenshot = pyautogui.screenshot(region=(x, y, width, height))
|
||
else:
|
||
# 全屏录制
|
||
screenshot = pyautogui.screenshot()
|
||
|
||
# 转换为numpy数组
|
||
frame = np.array(screenshot)
|
||
|
||
# 转换颜色格式 (RGB -> BGR)
|
||
frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
|
||
|
||
# 写入视频文件
|
||
self.video_writer.write(frame)
|
||
|
||
# 控制帧率
|
||
time.sleep(1.0 / self.fps)
|
||
|
||
def pause_recording(self):
|
||
"""
|
||
暂停录制
|
||
"""
|
||
if not self.recording:
|
||
print("当前没有在录制")
|
||
return
|
||
|
||
self.paused = not self.paused
|
||
status = "暂停" if self.paused else "继续"
|
||
print(f"录制{status}")
|
||
|
||
def stop_recording(self):
|
||
"""
|
||
停止录制
|
||
"""
|
||
if not self.recording:
|
||
print("当前没有在录制")
|
||
return
|
||
|
||
self.recording = False
|
||
self.paused = False
|
||
|
||
# 等待录制线程结束
|
||
if self.thread:
|
||
self.thread.join()
|
||
|
||
# 释放视频写入器
|
||
if self.video_writer:
|
||
self.video_writer.release()
|
||
self.video_writer = None
|
||
|
||
print(f"录制完成: {self.output_path}")
|
||
|
||
def set_region(self, region):
|
||
"""
|
||
设置录制区域
|
||
|
||
Args:
|
||
region (tuple): 录制区域 (x, y, width, height),None表示全屏录制
|
||
"""
|
||
if self.recording:
|
||
print("录制进行中,无法更改区域设置")
|
||
return False
|
||
|
||
self.region = region
|
||
|
||
# 重新计算录制区域
|
||
if self.region:
|
||
x, y, width, height = self.region
|
||
# 确保区域在屏幕范围内
|
||
x = max(0, min(x, self.screen_size[0] - 1))
|
||
y = max(0, min(y, self.screen_size[1] - 1))
|
||
width = min(width, self.screen_size[0] - x)
|
||
height = min(height, self.screen_size[1] - y)
|
||
self.region = (x, y, width, height)
|
||
self.record_size = (width, height)
|
||
print(f"录制区域已设置: {self.region}")
|
||
else:
|
||
self.record_size = self.screen_size
|
||
print("录制模式已设置: 全屏录制")
|
||
|
||
return True
|
||
|
||
def get_status(self):
|
||
"""
|
||
获取录制状态
|
||
|
||
Returns:
|
||
dict: 包含录制状态信息的字典
|
||
"""
|
||
return {
|
||
'recording': self.recording,
|
||
'paused': self.paused,
|
||
'output_path': getattr(self, 'output_path', None),
|
||
'screen_size': self.screen_size,
|
||
'record_size': self.record_size,
|
||
'region': self.region,
|
||
'fps': self.fps
|
||
}
|
||
|
||
def main():
|
||
"""
|
||
主函数 - 命令行界面
|
||
"""
|
||
recorder = ScreenRecorder()
|
||
|
||
print("\n=== 屏幕录制工具 ===")
|
||
print("命令:")
|
||
print(" start [filename] - 开始录制")
|
||
print(" pause - 暂停/恢复录制")
|
||
print(" stop - 停止录制")
|
||
print(" status - 查看状态")
|
||
print(" region x y w h - 设置录制区域 (x, y, 宽度, 高度)")
|
||
print(" fullscreen - 设置全屏录制")
|
||
print(" center w h - 设置居中区域录制 (宽度, 高度)")
|
||
print(" quit - 退出程序")
|
||
print("\n输入命令:")
|
||
|
||
while True:
|
||
try:
|
||
command = input("> ").strip().split()
|
||
|
||
if not command:
|
||
continue
|
||
|
||
cmd = command[0].lower()
|
||
|
||
if cmd == "start":
|
||
filename = command[1] if len(command) > 1 else None
|
||
recorder.start_recording(filename)
|
||
|
||
elif cmd == "pause":
|
||
recorder.pause_recording()
|
||
|
||
elif cmd == "stop":
|
||
recorder.stop_recording()
|
||
|
||
elif cmd == "status":
|
||
status = recorder.get_status()
|
||
print(f"录制状态: {'进行中' if status['recording'] else '已停止'}")
|
||
print(f"暂停状态: {'是' if status['paused'] else '否'}")
|
||
print(f"输出文件: {status['output_path']}")
|
||
print(f"屏幕尺寸: {status['screen_size']}")
|
||
print(f"录制尺寸: {status['record_size']}")
|
||
print(f"录制区域: {status['region'] if status['region'] else '全屏'}")
|
||
print(f"帧率: {status['fps']} FPS")
|
||
|
||
elif cmd == "region":
|
||
if len(command) != 5:
|
||
print("用法: region x y width height")
|
||
continue
|
||
try:
|
||
x, y, w, h = map(int, command[1:5])
|
||
if recorder.set_region((x, y, w, h)):
|
||
print(f"录制区域已设置: ({x}, {y}, {w}, {h})")
|
||
except ValueError:
|
||
print("请输入有效的数字")
|
||
|
||
elif cmd == "fullscreen":
|
||
if recorder.set_region(None):
|
||
print("已设置为全屏录制")
|
||
|
||
elif cmd == "center":
|
||
if len(command) != 3:
|
||
print("用法: center width height")
|
||
continue
|
||
try:
|
||
w, h = map(int, command[1:3])
|
||
screen_w, screen_h = recorder.screen_size
|
||
x = (screen_w - w) // 2
|
||
y = (screen_h - h) // 2
|
||
if recorder.set_region((x, y, w, h)):
|
||
print(f"居中录制区域已设置: ({x}, {y}, {w}, {h})")
|
||
except ValueError:
|
||
print("请输入有效的数字")
|
||
|
||
elif cmd == "quit":
|
||
if recorder.recording:
|
||
recorder.stop_recording()
|
||
print("程序退出")
|
||
break
|
||
|
||
else:
|
||
print("未知命令")
|
||
|
||
except KeyboardInterrupt:
|
||
if recorder.recording:
|
||
recorder.stop_recording()
|
||
print("\n程序退出")
|
||
break
|
||
except Exception as e:
|
||
print(f"错误: {e}")
|
||
|
||
if __name__ == "__main__":
|
||
main() |