add components
parent
3a38df12a9
commit
30205c86c9
|
@ -2,10 +2,22 @@ import re
|
|||
import RunCMD
|
||||
from RunCMD import run_cmd
|
||||
import cchardet
|
||||
import logging
|
||||
|
||||
class DevicesInfo():
|
||||
|
||||
def __init__(self):
|
||||
|
||||
#日志
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.logger.setLevel(level = logging.INFO)
|
||||
handler = logging.FileHandler('log.txt')
|
||||
handler.setLevel(logging.INFO)
|
||||
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||
handler.setFormatter(formatter)
|
||||
|
||||
self.logger.addHandler(handler)
|
||||
|
||||
list_devices_cmd = 'ffmpeg -list_devices true -f dshow -i dummy'
|
||||
# status, output = subprocess.getstatusoutput(list_devices_cmd)
|
||||
output_err, output_str = run_cmd(list_devices_cmd, universal_newlines = False)
|
||||
|
@ -33,11 +45,15 @@ class DevicesInfo():
|
|||
device_line = []
|
||||
devices_output_copy = devices_output[0:]
|
||||
devices_txt = ''
|
||||
self.logger.info('devices_text:')
|
||||
for txt in devices_output_copy:
|
||||
# charset = cchardet.detect(txt)
|
||||
# if charset
|
||||
devices_txt += txt.decode('utf-8','ignore').replace(r'\r\n','')
|
||||
self.logger.info(txt)
|
||||
devices_txt += txt.decode('utf-8').replace(r'\r\n','')
|
||||
# print(dir(re))
|
||||
self.logger.info('decode result:')
|
||||
self.logger.info(devices_txt)
|
||||
print(devices_txt)
|
||||
results = re.findall(r'\[[^\]]+\]([^\[]+)',devices_txt)
|
||||
# results.pop(0)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# from DevicesInfo import *
|
||||
|
||||
class RecordConfig():
|
||||
def __init__(self, config_file_name = 'config.ini'):
|
||||
def __init__(self, config_file_name = 'configByLinxiao.ini'):
|
||||
self.file_name = config_file_name
|
||||
self.encoding = 'gb2312'
|
||||
self.load()
|
||||
|
@ -41,6 +41,11 @@ class RecordConfig():
|
|||
conf.set(record_section_name,'vcodec','libx264')
|
||||
conf.set(record_section_name,'frame_rate','7.0')
|
||||
conf.set(record_section_name,'file_dir','.')
|
||||
conf.set(record_section_name,'threads', '4')
|
||||
|
||||
conf.add_section('author')
|
||||
conf.set('author', 'name', 'linxiao')
|
||||
conf.set('author', 'mail', '940950943@qq.com')
|
||||
|
||||
self.config = conf
|
||||
self.write()
|
||||
|
|
|
@ -19,22 +19,20 @@ class RecordHelp(QWidget):
|
|||
self.setObjectName("Form")
|
||||
self.resize(386, 238)
|
||||
icon = QtGui.QIcon()
|
||||
icon.addPixmap(QtGui.QPixmap("resource/gutin.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
icon.addPixmap(QtGui.QPixmap("../record-camera-and-screen/resource/gutin.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.setWindowIcon(icon)
|
||||
self.frame = QtWidgets.QFrame(self)
|
||||
self.frame.setGeometry(QtCore.QRect(60, 330, 229, 10))
|
||||
self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
|
||||
self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
|
||||
self.frame.setObjectName("frame")
|
||||
self.gridLayoutWidget = QtWidgets.QWidget(self)
|
||||
self.gridLayoutWidget.setGeometry(QtCore.QRect(19, 19, 341, 201))
|
||||
self.gridLayoutWidget.setObjectName("gridLayoutWidget")
|
||||
self.gridLayout = QtWidgets.QGridLayout(self.gridLayoutWidget)
|
||||
self.gridLayout.setContentsMargins(0, 0, 0, 0)
|
||||
self.gridLayout.setObjectName("gridLayout")
|
||||
self.verticalLayout = QtWidgets.QVBoxLayout()
|
||||
self.verticalLayoutWidget = QtWidgets.QWidget(self)
|
||||
self.verticalLayoutWidget.setGeometry(QtCore.QRect(0, 0, 391, 241))
|
||||
self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
|
||||
self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
|
||||
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
self.label = QtWidgets.QLabel(self.gridLayoutWidget)
|
||||
self.label = QtWidgets.QLabel(self.verticalLayoutWidget)
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(14)
|
||||
font.setBold(True)
|
||||
|
@ -43,31 +41,50 @@ class RecordHelp(QWidget):
|
|||
self.label.setAlignment(QtCore.Qt.AlignCenter)
|
||||
self.label.setObjectName("label")
|
||||
self.verticalLayout.addWidget(self.label)
|
||||
self.label_2 = QtWidgets.QLabel(self.gridLayoutWidget)
|
||||
self.label_2 = QtWidgets.QLabel(self.verticalLayoutWidget)
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(12)
|
||||
font.setBold(True)
|
||||
font.setWeight(75)
|
||||
self.label_2.setFont(font)
|
||||
self.label_2.setAlignment(QtCore.Qt.AlignCenter)
|
||||
self.label_2.setObjectName("label_2")
|
||||
self.verticalLayout.addWidget(self.label_2)
|
||||
self.label_3 = QtWidgets.QLabel(self.gridLayoutWidget)
|
||||
self.label_3 = QtWidgets.QLabel(self.verticalLayoutWidget)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Agency FB")
|
||||
font.setPointSize(12)
|
||||
font.setBold(True)
|
||||
font.setWeight(75)
|
||||
self.label_3.setFont(font)
|
||||
self.label_3.setAlignment(QtCore.Qt.AlignCenter)
|
||||
self.label_3.setObjectName("label_3")
|
||||
self.verticalLayout.addWidget(self.label_3)
|
||||
self.label_4 = QtWidgets.QLabel(self.gridLayoutWidget)
|
||||
self.label_4 = QtWidgets.QLabel(self.verticalLayoutWidget)
|
||||
self.label_4.setBaseSize(QtCore.QSize(0, 0))
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(12)
|
||||
font.setBold(True)
|
||||
font.setWeight(75)
|
||||
self.label_4.setFont(font)
|
||||
self.label_4.setAlignment(QtCore.Qt.AlignCenter)
|
||||
self.label_4.setObjectName("label_4")
|
||||
self.verticalLayout.addWidget(self.label_4)
|
||||
self.gridLayout.addLayout(self.verticalLayout, 0, 0, 1, 1)
|
||||
self.label_5 = QtWidgets.QLabel(self.verticalLayoutWidget)
|
||||
font = QtGui.QFont()
|
||||
font.setBold(True)
|
||||
font.setWeight(75)
|
||||
self.label_5.setFont(font)
|
||||
self.label_5.setAlignment(QtCore.Qt.AlignCenter)
|
||||
self.label_5.setObjectName("label_5")
|
||||
self.verticalLayout.addWidget(self.label_5)
|
||||
self.label_6 = QtWidgets.QLabel(self.verticalLayoutWidget)
|
||||
font = QtGui.QFont()
|
||||
font.setBold(True)
|
||||
font.setWeight(75)
|
||||
self.label_6.setFont(font)
|
||||
self.label_6.setAlignment(QtCore.Qt.AlignCenter)
|
||||
self.label_6.setObjectName("label_6")
|
||||
self.verticalLayout.addWidget(self.label_6)
|
||||
|
||||
self.retranslateUi()
|
||||
QtCore.QMetaObject.connectSlotsByName(self)
|
||||
|
@ -77,8 +94,10 @@ class RecordHelp(QWidget):
|
|||
self.setWindowTitle(_translate("Form", "帮助"))
|
||||
self.label.setText(_translate("Form", "Gutin谷田会议视频录播管理系统"))
|
||||
self.label_2.setText(_translate("Form", "主要功能:录制屏幕和摄像头"))
|
||||
self.label_3.setText(_translate("Form", "开发者:林潇 "))
|
||||
self.label_4.setText(_translate("Form", "联系方式:940950943@qq.com"))
|
||||
self.label_3.setText(_translate("Form", "默认快捷键:"))
|
||||
self.label_4.setText(_translate("Form", "录制摄像头:Ctrl + Shfit + Alt + A"))
|
||||
self.label_5.setText(_translate("Form", "录制屏幕:Ctrl + Shift + Alt + B"))
|
||||
self.label_6.setText(_translate("Form", "停止录制:Ctrl + Shift + Alt + C"))
|
||||
|
||||
def showWindow(self):
|
||||
if not self.isVisible():
|
||||
|
|
|
@ -11,7 +11,7 @@ import RecordConfig
|
|||
from RecordConfig import *
|
||||
import logging
|
||||
import RunCMD
|
||||
from RunCMD import run_cmd
|
||||
from RunCMD import get_ffmpeg_path
|
||||
|
||||
class RecordVideo():
|
||||
|
||||
|
@ -72,6 +72,8 @@ class RecordVideo():
|
|||
self.brate=rc.config.getfloat('record','frame_rate')
|
||||
#文件目录
|
||||
self.file_dir= os.path.abspath(rc.config.get('record','file_dir'))
|
||||
#线程数
|
||||
self.threads = rc.config.getint('record','threads')
|
||||
|
||||
self.logger.info('camera device name: %s' % self.camera_name)
|
||||
self.logger.info('voice device name: %s' % self.voice_device_name)
|
||||
|
@ -83,27 +85,6 @@ class RecordVideo():
|
|||
self.logger.info('save dir: %s' % self.file_dir)
|
||||
|
||||
|
||||
def get_ffmpeg_path(self):
|
||||
|
||||
datadir = ''
|
||||
subdir = os.path.join('ffmpeg-shared','bin')
|
||||
|
||||
if getattr(sys, 'frozen', False):
|
||||
# The application is frozen
|
||||
datadir = os.path.dirname(sys.executable)
|
||||
else:
|
||||
# The application is not frozen
|
||||
# Change this bit to match where you store your data files:
|
||||
datadir = os.path.dirname(__file__)
|
||||
datadir = os.path.join(datadir, subdir)
|
||||
datadir = os.path.abspath(datadir)
|
||||
print(datadir)
|
||||
#如果存在ffmpeg.exe
|
||||
if os.path.isfile(os.path.join(datadir, 'ffmpeg.exe')):
|
||||
return datadir
|
||||
#兼容环境变量设置
|
||||
return ''
|
||||
|
||||
def start_ffmpeg(self,cmd, shell = True):
|
||||
|
||||
try:
|
||||
|
@ -159,17 +140,17 @@ class RecordVideo():
|
|||
# self.logger.info(self.process.communicate())
|
||||
# print('done.')
|
||||
|
||||
def record(self,cmd='ffmpeg -h', target = None):
|
||||
def record(self, cmd='ffmpeg -h', target = None):
|
||||
|
||||
if target:
|
||||
cmd = os.path.join(self.get_ffmpeg_path(), cmd)
|
||||
cmd = os.path.join(get_ffmpeg_path(), cmd)
|
||||
print('cmd: \n%s' % cmd)
|
||||
self.logger.info('record cmd:\n %s' % cmd)
|
||||
th=Thread(name=self.record_thread_name, target= target, args = (cmd,), daemon=True)
|
||||
self.record_thread = Thread(name=self.record_thread_name, target= target, args = (cmd,), daemon=True)
|
||||
th.start()
|
||||
self.recording=True
|
||||
self.exception_exit = False
|
||||
self.record_thread=th
|
||||
|
||||
print('record thread,ident:%d' % self.record_thread.ident)
|
||||
# th.join()
|
||||
|
||||
|
@ -199,12 +180,13 @@ class RecordVideo():
|
|||
if self.camera_name and self.voice_device_name:
|
||||
|
||||
self.record_type=RecordType.Camera
|
||||
record_cmd='ffmpeg -f dshow -i video=\"%s\":audio=\"%s\" -acodec libmp3lame -vcodec %s -preset:v ultrafast -tune:v zerolatency -s %s -r %d -y %s' %(
|
||||
record_cmd='ffmpeg -f dshow -i video=\"%s\":audio=\"%s\" -acodec libmp3lame -vcodec %s -preset:v ultrafast -tune:v zerolatency -s %s -r %d -threads %d -y %s' %(
|
||||
self.deal_with_device_name(self.camera_name),
|
||||
self.deal_with_device_name(self.voice_device_name),
|
||||
self.video_codec,
|
||||
self.resolution,
|
||||
self.brate,
|
||||
self.threads,
|
||||
self.get_file_name()
|
||||
)
|
||||
# print(record_cmd)
|
||||
|
@ -213,16 +195,43 @@ class RecordVideo():
|
|||
def record_screen(self):
|
||||
if self.screen_name and self.system_voice_device_name:
|
||||
self.record_type=RecordType.Screen
|
||||
record_cmd='ffmpeg -f dshow -i video="{}":audio="{}" -acodec libmp3lame -vcodec {} -preset:v ultrafast -tune:v zerolatency -s {} -r {} -y {}'.format(
|
||||
record_cmd='ffmpeg -f dshow -i video="{}":audio="{}" -acodec libmp3lame -vcodec {} -preset:v ultrafast -tune:v zerolatency -s {} -r {} -threads {} -y {}'.format(
|
||||
self.deal_with_device_name(self.screen_name),
|
||||
self.deal_with_device_name(self.system_voice_device_name),
|
||||
self.video_codec,
|
||||
self.resolution,
|
||||
'1024x768', #屏幕录制分辨率固定
|
||||
self.brate,
|
||||
self.threads,
|
||||
self.get_file_name()
|
||||
)
|
||||
self.record(record_cmd, self.start_ffmpeg)
|
||||
|
||||
|
||||
def check_device(self):
|
||||
#简单验证摄像头设置是否为空
|
||||
ready = True
|
||||
l_msg = ''
|
||||
if not self.camera_name:
|
||||
ready = False
|
||||
l_msg += '摄像头设备为空\n'
|
||||
|
||||
if not self.voice_device_name:
|
||||
ready = False
|
||||
l_msg += '麦克风设备为空\n'
|
||||
|
||||
if not self.screen_name:
|
||||
ready = False
|
||||
l_msg += '屏幕录制驱动为空\n'
|
||||
|
||||
if not self.system_voice_device_name:
|
||||
ready = False
|
||||
l_msg += '系统声音录制驱动为空\n'
|
||||
|
||||
if ready:
|
||||
l_msg = '设备检测正常'
|
||||
print(l_msg)
|
||||
self.logger.info(l_msg)
|
||||
return ready
|
||||
|
||||
|
||||
def debug_camera(self):
|
||||
try:
|
||||
|
@ -252,14 +261,18 @@ class RecordVideo():
|
|||
return device_name
|
||||
|
||||
def get_file_name(self):
|
||||
time_str=datetime.now().strftime('%Y%m%d_%H%M%S')
|
||||
video_type = ''
|
||||
date_dir = datetime.now().strftime('%Y-%m-%d')
|
||||
time_str = datetime.now().strftime('%H_%M')
|
||||
video_type_name = ''
|
||||
if self.record_type == RecordType.Camera:
|
||||
video_type = '摄像头'
|
||||
video_type_name = '摄像头'
|
||||
if self.record_type == RecordType.Screen:
|
||||
video_type = '屏幕'
|
||||
video_type_name = '屏幕'
|
||||
|
||||
file_name = os.path.join(self.file_dir, '{}_{}{}'.format(video_type, time_str, self.file_suffix))
|
||||
today_file_dir = os.path.join(self.file_dir, date_dir)
|
||||
if not os.path.exists(today_file_dir):
|
||||
os.mkdir(today_file_dir)
|
||||
file_name = os.path.join(today_file_dir, '{}-{}-{}{}'.format(video_type_name, time_str, self.resolution, self.file_suffix))
|
||||
print('recording file name: %s' % file_name)
|
||||
return file_name
|
||||
|
||||
|
|
|
@ -303,16 +303,30 @@ class RecordWindow(QtWidgets.QWidget):
|
|||
print('检测到正在录制,录制类型将切换...')
|
||||
self.stop_record()
|
||||
|
||||
#开始录制
|
||||
if rtype == RecordType.Camera:
|
||||
print('开始录制摄像头...')
|
||||
self.rv.record_camera()
|
||||
elif rtype == RecordType.Screen:
|
||||
print('开始录制屏幕...')
|
||||
self.rv.record_screen()
|
||||
|
||||
self.start_timer()
|
||||
self.update_state()
|
||||
if self.rv.check_device():
|
||||
#开始录制
|
||||
if rtype == RecordType.Camera:
|
||||
print('开始录制摄像头...')
|
||||
self.rv.record_camera()
|
||||
elif rtype == RecordType.Screen:
|
||||
print('开始录制屏幕...')
|
||||
self.rv.record_screen()
|
||||
|
||||
self.start_timer()
|
||||
self.update_state()
|
||||
else:
|
||||
|
||||
question = QMessageBox.information(self, '提示', '检测到录制设备缺失,无法进行录制,请先完善设备设置。', QMessageBox.Yes)
|
||||
# question = QMessageBox()
|
||||
# question.setText('检测到录制设备缺失,无法进行录制,请先完善设备设置。')
|
||||
# question.setWindowTitle('提示')
|
||||
# question.setIcon(QMessageBox.Question)
|
||||
# question.addButton(QMessageBox.Yes)
|
||||
# question.addButton(QMessageBox.No)
|
||||
# question.setDefaultButton(QMessageBox.No)
|
||||
# ret = question.exec()
|
||||
# if ret == QMessageBox.Yes:
|
||||
# print('软件将退出.')
|
||||
|
||||
''''
|
||||
鼠标拖动窗体
|
||||
|
|
26
RunCMD.py
26
RunCMD.py
|
@ -1,8 +1,10 @@
|
|||
import subprocess
|
||||
import subprocess,os,sys
|
||||
|
||||
def run_cmd(cmd, shell = True, universal_newlines = False):
|
||||
output_info=[]
|
||||
output_err=[]
|
||||
cmd = os.path.join(get_ffmpeg_path(), cmd)
|
||||
print('执行cmd:\n%s' % cmd)
|
||||
with subprocess.Popen(cmd,
|
||||
shell = shell,
|
||||
universal_newlines = universal_newlines,
|
||||
|
@ -26,4 +28,26 @@ def run_cmd(cmd, shell = True, universal_newlines = False):
|
|||
|
||||
return output_err, output_info
|
||||
|
||||
|
||||
def get_ffmpeg_path():
|
||||
|
||||
datadir = ''
|
||||
subdir = os.path.join('ffmpeg-shared','bin')
|
||||
|
||||
if getattr(sys, 'frozen', False):
|
||||
# The application is frozen
|
||||
datadir = os.path.dirname(sys.executable)
|
||||
else:
|
||||
# The application is not frozen
|
||||
# Change this bit to match where you store your data files:
|
||||
datadir = os.path.dirname(__file__)
|
||||
datadir = os.path.join(datadir, subdir)
|
||||
datadir = os.path.abspath(datadir)
|
||||
print(datadir)
|
||||
#如果存在ffmpeg.exe
|
||||
if os.path.isfile(os.path.join(datadir, 'ffmpeg.exe')):
|
||||
return datadir
|
||||
#兼容环境变量设置
|
||||
return ''
|
||||
|
||||
|
|
@ -173,7 +173,7 @@ class SettingWindow(QDialog):
|
|||
|
||||
def retranslateUi(self):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
self.setWindowTitle(_translate("Form", "设置-Development By Linxiao"))
|
||||
self.setWindowTitle(_translate("Form", "设置"))
|
||||
self.label_3.setText(_translate("Form", "屏幕录制设备:"))
|
||||
self.label_2.setText(_translate("Form", "声音输入设备:"))
|
||||
self.label.setText(_translate("Form", "摄像头名称:"))
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
[devices]
|
||||
camera_device_name =
|
||||
voice_device_name =
|
||||
screen_device_name =
|
||||
system_voice_device_name =
|
||||
|
||||
[shortcut]
|
||||
camera = 160,162,164,65
|
||||
screen = 160,162,164,66
|
||||
stop = 160,162,164,67
|
||||
|
||||
[record]
|
||||
resolution = 1920x1080
|
||||
vcodec = libx264
|
||||
frame_rate = 7.0
|
||||
file_dir = .
|
||||
threads = 4
|
||||
|
||||
[author]
|
||||
name = linxiao
|
||||
mail = 940950943@qq.com
|
||||
|
|
@ -2,8 +2,8 @@ import sys
|
|||
from cx_Freeze import setup, Executable
|
||||
|
||||
# Dependencies are automatically detected, but it might need fine tuning.
|
||||
build_exe_options = { 'include_files':['resource','ffmpeg-shared']}
|
||||
install_exe_options = { 'install_dir' : 'd:\\record-camera-and-screen', 'build_dir':'build', 'install_exe':'d:\\record-camera-and-screen'}
|
||||
build_exe_options = { 'include_files':['resource','ffmpeg-shared'], 'build_exe' : 'd:/dev/record/record-win'}
|
||||
install_exe_options = { 'install_dir' : 'd:/dev/record/record-win', 'build_dir':'build', 'install_exe':'d:\\record-camera-and-screen'}
|
||||
|
||||
# GUI applications require a different base on Windows (the default is for a
|
||||
# console application).
|
||||
|
@ -12,7 +12,7 @@ if sys.platform == "win32":
|
|||
base = "Win32GUI"
|
||||
|
||||
executables = [
|
||||
Executable('RecordWindow.py', base=base, icon = 'resource/gutin.ico', targetName = 'RecordWindow.exe')
|
||||
Executable('RecordWindow.py', base=base, icon = 'resource/gutin.ico')
|
||||
]
|
||||
setup( name = "Gutin谷田会议视频录播管理系统",
|
||||
version = "0.2",
|
||||
|
|
Loading…
Reference in New Issue