第一次在CSDN写博客,有点小紧张(/ω\)
以下内容完全个人理解,有错误请指出~
最近在用python做一个小工具包,这两天想加入录屏功能了;就在网上搜罗了一大堆python录屏的代码,思路都差不多,除了ffmpeg的方法其他都是用先截屏在用opencv生成视频的,ffmpeg的方法因为过于复杂我就不研究了,其他的主要有PLC+opencv的、pyqt5+opencv的、还有本篇文章用到的pywin32的方法前面的两种其实都可以实现(我的小工具包就是用pyqt5截屏的),但相对于pywin32来说都是太!慢!了!
这个录屏工具还有我正在做的工具包都是主要利用快捷键功能启动的,键盘监听就用了pynput,话不多说,上后台监听部分代码:
from pynput import keyboard
ALT = False
Z = False
X = False
C = False
#本文中只用到ALT和C其他的备用(在我的小工具包里ALT+Z是截屏、ALT+x是截屏文字识别)自行扩展
def listen(): # 键盘监听函数
print("listening")
def on_press(key):
global ALT, Z, X, C, start_time, real_fps
if key == keyboard.Key.alt or key == keyboard.Key.alt_l or key == keyboard.Key.alt_r:
ALT = True
if key == keyboard.KeyCode(char='z') or key == keyboard.KeyCode(char='Z'):
Z = True
if key == keyboard.KeyCode(char='x') or key == keyboard.KeyCode(char='X'):
X = True
if key == keyboard.KeyCode(char='c') or key == keyboard.KeyCode(char='C'):
C = True
# print(ALT, Z, X, C)
if ALT and C: # 检测到Alt和c同时按下时,启动/关闭录屏
ALT = C = False
record.recording = not record.recording
print(11)
# startt = time.process_time()
if record.recording:
start_time = time.process_time() # 记录录屏开始时间
startrecord = StartRecord()
startrecord.start()
print("录屏开始")
else:
print("录制时间" + str(time.process_time() - start_time))
real_fps = i / (time.process_time() - start_time) # 计算真实帧率
print("fps" + str(real_fps))
def on_release(key):
global ALT, Z, X, C
if key == keyboard.Key.alt or key == keyboard.Key.alt_l or key == keyboard.Key.alt_r:
ALT = False
if key == keyboard.KeyCode(char='z') or key == keyboard.KeyCode(char='Z'):
Z = False
if key == keyboard.KeyCode(char='x') or key == keyboard.KeyCode(char='X'):
X = False
if key == keyboard.KeyCode(char='c') or key == keyboard.KeyCode(char='C'):
C = False
with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:
listener.join()
class ListenThread(Thread): # 截屏监听线程,监听函数一定要放在后台线程里康康说明那个join了吗( ̄︶ ̄)↗
def __init__(self):
super().__init__()
def run(self):
listen()
if __name__ == "__main__":
listenThread = ListenThread() # 创建监听线程
listenThread.start()
这个应该很好懂的,不懂就看官方文档吧https://pynput.readthedocs.io/en/latest/index.html
(゜ー゜)毕竟我也是这么看过来的…
下面是录屏主体,我把它放在一个类中(我也不知道为撒,直觉告诉我放在类中好用一点点)
import win32con
import win32gui
import win32ui
class Recordingthescreen():
def __init__(self):
self.recording = False
self.recorded = False
self.recorded2 = False
self.namelist = []
self.fps = real_fps
self.size = (500, 500) # 截屏宽度,改变这个可以自定义录屏范围
self.x = 0
self.y = 0 # 截屏开始点,改变这个可以自定义录屏范围
self.videoWriter = cv2.VideoWriter('t/TestVideo.avi', cv2.VideoWriter_fourcc(*'XVID'),
self.fps,
self.size) # *'XVID'MPEG-4编码 *'mp4v' *'PIMI' MPEG-1编码 *'I420'(无损压缩avi
def window_capture(self, filename, x, y, w, h):
hwnd = 0 # 窗口的编号,0号表示当前活跃窗口
# 根据窗口句柄获取窗口的设备上下文DC(Divice Context)
hwndDC = win32gui.GetWindowDC(hwnd)
# 根据窗口的DC获取mfcDC
mfcDC = win32ui.CreateDCFromHandle(hwndDC)
# mfcDC创建可兼容的DC
saveDC = mfcDC.CreateCompatibleDC()
# 创建bigmap准备保存图片
saveBitMap = win32ui.CreateBitmap()
# 为bitmap开辟空间
saveBitMap.CreateCompatibleBitmap(mfcDC, w, h)
# 高度saveDC,将截图保存到saveBitmap中
saveDC.SelectObject(saveBitMap)
# 截取从左上角(0,0)长宽为(w,h)的图片
saveDC.BitBlt((0, 0), (w, h), mfcDC, (x, y), win32con.SRCCOPY)
saveBitMap.SaveBitmapFile(saveDC, filename)
win32gui.DeleteObject(saveBitMap.GetHandle())
saveDC.DeleteDC()
mfcDC.DeleteDC()
win32gui.ReleaseDC(hwnd, hwndDC)
def record(self):
global i
i = 0
prossthread = FileThread() # 视频合成线程
prossthread.start()
if self.recording:
self.videoWriter = cv2.VideoWriter('t/TestVideo.avi', cv2.VideoWriter_fourcc(*'XVID'),
self.fps,
self.size) # *'XVID'MPEG-4编码 *'mp4v' *'PIMI' MPEG-1编码 *'I420'(无损压缩avi
x = self.x
y = self.y
w = self.size[0]
h = self.size[1]
print("creatwriter")
while self.recording:
t1 = time.process_time()
self.window_capture("t/haha{0}.png".format(i), x, y, w, h)
namelist.append("t/haha{0}.png".format(i))
i += 1
self.recorded = True
print(str(i) + '\t' + str(time.process_time() - t1))
print("截屏结束,继续处理中")
if not self.recording and self.recorded:
self.recorded2 = True
prossthread.join(200) # 等待原始视频合成处理完毕
self.videoWriter.release() # 释放资源
print("视频帧率修正中...")
# 根据真实帧率处理原始视频
videoWriter2 = cv2.VideoWriter('t/output' + str(time.time()) + '.mp4', cv2.VideoWriter_fourcc(*'mp4v'),
real_fps,
self.size) # *'XVID'MPEG-4编码 *'mp4v' *'PIMI' MPEG-1编码 *'I420'(无损压缩avi
videoCapture = cv2.VideoCapture('t/TestVideo.avi')
success, frame = videoCapture.read()#从原始视频中逐帧读取
while success:
success, frame = videoCapture.read()
videoWriter2.write(frame) # 重写视频,修正帧率
videoWriter2.release()
print("视频导出成功")
self.recorded = False
self.recorded2 = False
namelist.clear()
i = 0 # 重置为0帧,为下一次录屏做准备```
hhh看注释应该都懂了的,我也懒得解释了…(打好多字好类( ̄ ‘i  ̄;)
其中涉及了一个修正帧率的步骤,因为不知道截屏总时间又要不占用截屏功能很难实时计算出帧率,我也是迫不得已。。
为避免线程堵塞,我又为启动录屏函数和后台处理图片(生成视频)函数创建了新线程:
class StartRecord(Thread): # 开始录屏线程
def __init__(self):
super().__init__()
def run(self):
record.record()
class FileThread(Thread): # 图片处理线程(生成原始视频)
def __init__(self):
super().__init__()
def run(self):
while 1:
if namelist:
frame = namelist.pop(0)
img2 = cv2.imread(frame)
record.videoWriter.write(img2)
os.remove(frame)
if not namelist and record.recorded2:
print("处理完毕")
record.videoWriter.release()
break
好了,现在我们把上面的各种类调用一下
(总体代码)
import os
import time
import win32con
import win32gui
import win32ui
import cv2
from threading import Thread
from pynput import keyboard
ALT = False
Z = False
X = False
C = False
#本文中只用到ALT和C其他的备用(在我的小工具包里ALT+Z是截屏、ALT+x是截屏文字识别)自行扩展
def listen(): # 键盘监听函数
print("listening")
def on_press(key):
global ALT, Z, X, C, start_time, real_fps
if key == keyboard.Key.alt or key == keyboard.Key.alt_l or key == keyboard.Key.alt_r:
ALT = True
if key == keyboard.KeyCode(char='z') or key == keyboard.KeyCode(char='Z'):
Z = True
if key == keyboard.KeyCode(char='x') or key == keyboard.KeyCode(char='X'):
X = True
if key == keyboard.KeyCode(char='c') or key == keyboard.KeyCode(char='C'):
C = True
# print(ALT, Z, X, C)
if ALT and C: # 录屏
ALT = C = False
record.recording = not record.recording
print(11)
# startt = time.process_time()
if record.recording:
start_time = time.process_time() # 记录录屏开始时间
startrecord = StartRecord()
startrecord.start()
print("录屏开始")
else:
print("录制时间" + str(time.process_time() - start_time))
real_fps = i / (time.process_time() - start_time) # 计算真实帧率
print("fps" + str(real_fps))
def on_release(key):
global ALT, Z, X, C
if key == keyboard.Key.alt or key == keyboard.Key.alt_l or key == keyboard.Key.alt_r:
ALT = False
if key == keyboard.KeyCode(char='z') or key == keyboard.KeyCode(char='Z'):
Z = False
if key == keyboard.KeyCode(char='x') or key == keyboard.KeyCode(char='X'):
X = False
if key == keyboard.KeyCode(char='c') or key == keyboard.KeyCode(char='C'):
C = False
with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:
listener.join()
class ListenThread(Thread): # 截屏监听线程
def __init__(self):
super().__init__()
def run(self):
listen()
class StartRecord(Thread): # 开始录屏线程
def __init__(self):
super().__init__()
def run(self):
record.record()
class FileThread(Thread): # 图片处理线程(生成原始视频)
def __init__(self):
super().__init__()
def run(self):
while 1:
if namelist:
frame = namelist.pop(0)
img2 = cv2.imread(frame)
record.videoWriter.write(img2)
os.remove(frame)
if not namelist and record.recorded2:
print("处理完毕")
record.videoWriter.release()
break
class Recordingthescreen():
def __init__(self):
self.recording = False
self.recorded = False
self.recorded2 = False
self.namelist = []
self.fps = real_fps
self.size = (500, 500) # 截屏宽度,改变这个可以自定义录屏范围
self.x = 0
self.y = 0 # 截屏开始点,改变这个可以自定义录屏范围
self.videoWriter = cv2.VideoWriter('t/TestVideo.avi', cv2.VideoWriter_fourcc(*'XVID'),
self.fps,
self.size) # *'XVID'MPEG-4编码 *'mp4v' *'PIMI' MPEG-1编码 *'I420'(无损压缩avi
def window_capture(self, filename, x, y, w, h):#这一段不知在哪里抄的(网上关于pywin32截屏只有这一个参考
hwnd = 0 # 窗口的编号,0号表示当前活跃窗口
# 根据窗口句柄获取窗口的设备上下文DC(Divice Context)
hwndDC = win32gui.GetWindowDC(hwnd)
# 根据窗口的DC获取mfcDC
mfcDC = win32ui.CreateDCFromHandle(hwndDC)
# mfcDC创建可兼容的DC
saveDC = mfcDC.CreateCompatibleDC()
# 创建bigmap准备保存图片
saveBitMap = win32ui.CreateBitmap()
# 为bitmap开辟空间
saveBitMap.CreateCompatibleBitmap(mfcDC, w, h)
# 高度saveDC,将截图保存到saveBitmap中
saveDC.SelectObject(saveBitMap)
# 截取从左上角(0,0)长宽为(w,h)的图片
saveDC.BitBlt((0, 0), (w, h), mfcDC, (x, y), win32con.SRCCOPY)
saveBitMap.SaveBitmapFile(saveDC, filename)
win32gui.DeleteObject(saveBitMap.GetHandle())
saveDC.DeleteDC()
mfcDC.DeleteDC()
win32gui.ReleaseDC(hwnd, hwndDC)
def record(self):
global i
i = 0
prossthread = FileThread() # 视频合成线程
prossthread.start()
if self.recording:
self.videoWriter = cv2.VideoWriter('t/TestVideo.avi', cv2.VideoWriter_fourcc(*'XVID'),
self.fps,
self.size) # *'XVID'MPEG-4编码 *'mp4v' *'PIMI' MPEG-1编码 *'I420'(无损压缩avi
x = self.x
y = self.y
w = self.size[0]
h = self.size[1]
print("creatwriter")
while self.recording:
t1 = time.process_time()
self.window_capture("t/haha{0}.png".format(i), x, y, w, h)
namelist.append("t/haha{0}.png".format(i))
i += 1
self.recorded = True
print(str(i) + '\t' + str(time.process_time() - t1))
print("截屏结束,继续处理中")
if not self.recording and self.recorded:
self.recorded2 = True
prossthread.join(200) # 等待原始视频合成处理完毕
self.videoWriter.release() # 释放资源
print("视频帧率修正中...")
# 根据真实帧率处理原始视频
videoWriter2 = cv2.VideoWriter('t/output' + str(time.time()) + '.mp4', cv2.VideoWriter_fourcc(*'mp4v'),
real_fps,
self.size) # *'XVID'MPEG-4编码 *'mp4v' *'PIMI' MPEG-1编码 *'I420'(无损压缩avi
videoCapture = cv2.VideoCapture('t/TestVideo.avi')
success, frame = videoCapture.read()#从原始视频中逐帧读取
while success:
success, frame = videoCapture.read()
videoWriter2.write(frame) # 重写视频,修正帧率
videoWriter2.release()
print("视频导出成功")
self.recorded = False
self.recorded2 = False
namelist.clear()
i = 0 # 重置为0帧,为下一次录屏做准备
if __name__ == "__main__":
if not os.path.exists("t"):
os.mkdir("t")
namelist = []
start_time = time.process_time()
real_fps = 10
i = 0
record = Recordingthescreen() # 录屏实例
listenThread = ListenThread() # 监听线程
listenThread.start()
好,本教程到此结束了(才怪)
怎么能不说说用法呢?
运行打印出listening,就可以按下键盘的Alt+C键,命令行会输出当前截取的图片数以及每一帧截取的时间(后台线程也会同时将图片写入视频文件中),再次按下Alt+C键可以终止录屏,等待后台线程处理完成,再根据计算的帧率和暂时输出的原始视频文件重新生成帧率对应的视频文件。输出完成后可以继续重新录屏,录全屏的话帧率会有点低,可以修改上述代码中的self.size = (500, 500) # 截屏宽度,改变这个可以自定义录屏范围 self.x = 0,self.y = 0 # 截屏开始点,改变这个可以自定义录屏范围,范围小一点可以有较大的帧率
关于用到的几个模块的安装:
其中pywin32最难安装了(我捣腾了好久)最终在
https://github.com/mhammond/pywin32/releases
里下载了对应包才搞好,其他的都可以Google一下就有。。。。
pywin32:
这里只提供python3.7对应版本(GitHub上下载是真的慢。。。)
https://download.csdn.net/download/Fandes_F/12143836
opencv:
pip install opencv-python
pynput:
pip install pynput
关于打包,opencv的打包后运行会报错把python目录下的
opencv_videoio_ffmpeg412_64.dll
样的dll文件打包进去就行(这个包是真的大)
win32ui与PyQt5会冲突,我的工具包就是用pyqt5做的界面,然后如果我把这个截屏(录屏)代码导入我的主程序中时,pyqt5在调用窗口处理函数(如hide()show())时就会卡顿。。。。
所以我的小工具就用不上这个录屏代码了╥﹏╥…现在是用了ffmpeg的流式视频处理功能直接录屏了,而且性能超超超强,占用比QQ录屏占用低一半以上,录屏质量也大幅提高(支持无损录屏!),ffmpeg教程还是以后在写吧。。。先放几张我在我的小工具里面用ffmpeg录屏的图hh(还能录gif!,良不良心?)
同时jamtools还支持自定义帧率、鼠标录制、输出格式、编码等,用H.264编码还可以调整录屏的码率,安装插件还能只录制系统声音!(这些都被我集成了~嘻)
ps(2020.4.9):目前已经集截屏(支持滚动截屏)、录屏、识屏、文字提取(批量/截屏)、翻译、格式转换(各种媒体的裁剪拼接转码提取等!)、控制(录制键鼠动作并自定义重复播放!就是按键精灵呐)、聊天(一个沙雕聊天机器人。。)还支持所有界面划屏提字翻译!而且完全免费无广告!网上大多数录屏软件都是付费的。。或者有广告。。而且只有70多M (暂时是0.7.5版)。。
点击文末链接或在在公众号机械酱的小黑屋可以获取!
软件更新请留意公众号‘机械酱的小黑屋’
本文源码
本文源码打包的可运行文件(.exe)
本文中提到的自己做的工具包JamTools:(截屏、录屏、文字识别、翻译、图像识别、右键画屏提字翻译、格式转换、按键精灵、聊天机器人等功能)欢迎体验(禁止用于二次开发、售卖等用途,版权归本作者所有!)
(tips:CSDN这个版本已经太旧啦,可以到下面github链接上面下载最新的安装包)
JamTools github链接:https://github.com/fandesfyf/JamTools
一个分割线:
(20200321更新)最近发现了pynput最新版(1.6.8)已经有现成的快捷键接口了,可以直接注册全局快捷键.
示例用法:
from pynput import keyboard
def listen():
print("listen")
def on_activate_screenshot():
print('+z pressed' )
def on_activate_ocr():
print('+x pressed' )
def on_activate_screenrecord():
print('alt+c')
def on_activate_actionrun():
print('z+x')
def on_activate_actionrecord():
print('x+c')
hotkey = keyboard.GlobalHotKeys({
'+z' : on_activate_screenshot,
'+x' : on_activate_ocr,
'+c' : on_activate_screenrecord,
'z+x': on_activate_actionrun,
'c+x': on_activate_actionrecord})
hotkey.start()
hotkey.wait()
hotkey.join()
print("end")
listen()
具体请看pynput最新的1.6.8官方文档
本文作者:机械酱&Fandes