record-camera-and-screen/RecordVideo.py

275 lines
11 KiB
Python
Raw Normal View History

2018-04-17 08:11:26 +00:00
import datetime,time,sys,os,signal,re
from datetime import datetime
import subprocess,threading
2018-04-18 02:13:29 +00:00
from subprocess import CalledProcessError
2018-04-17 08:11:26 +00:00
# from multiprocessing import Process
from threading import Thread
import ctypes,inspect
import RecordType
from RecordType import *
import RecordConfig
from RecordConfig import *
import logging
import RunCMD
from RunCMD import run_cmd
class RecordVideo():
'''
ffmpeg -f dshow -i video="@device_pnp_\\\\?\\usb#vid_04f2&pid_b354&mi_00#7&30d7ad30&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global":audio="@device_cm_{33D9A762-90C8-11D0-BD43-00A0C911CE86}\wave_{571529B3-7DB3-42A3-ADEF-BBD82925C15D}" -acodec libmp3lame -vcodec libx265 -preset:v ultrafast -tune:v zerolatency -s 1920x1080 -r 7 -y record_camera_20180408_182541.mkv
'''
def __init__(self, record_video=True, record_voice=True):
# print('视频录制初始化中...')
# self.record_video=record_video
# self.record_voice=record_voice
#录制状态
self.recording=False
self.record_type=RecordType.Camera
#文件名称
self.file_name='record'
#文件后缀
self.file_suffix='.mkv'
self.process = None
self.record_thread_name='record'
self.record_thread=None
self.file_dir = ''
self.load_config()
def load_config(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)
rc = RecordConfig()
self.config = rc.config
#摄像头名称
self.camera_name=rc.config.get('devices','camera_device_name')
#麦克风名称
self.voice_device_name=rc.config.get('devices','voice_device_name')
#录制屏幕名称
self.screen_name=rc.config.get('devices','screen_device_name')
#系统声音设备名称
self.system_voice_device_name=rc.config.get('devices','system_voice_device_name')
#视频编码
self.video_codec=rc.config.get('record','vcodec')
#分辨率
self.resolution=rc.config.get('record','resolution')
#帧率
self.brate=rc.config.getfloat('record','frame_rate')
#文件目录
self.file_dir= os.path.abspath(rc.config.get('record','file_dir'))
self.logger.info('camera device name: %s' % self.camera_name)
self.logger.info('voice device name: %s' % self.voice_device_name)
self.logger.info('screen device name: %s' % self.screen_name)
self.logger.info('system voice device name: %s' % self.system_voice_device_name)
self.logger.info('vcodec: %s' % self.video_codec)
self.logger.info('resolution: %s' % self.resolution)
self.logger.info('frame rate: %s' % self.brate)
self.logger.info('save dir: %s' % self.file_dir)
def start_ffmpeg(self,cmd, shell = True):
2018-04-18 02:13:29 +00:00
try:
print('录制中...')
self.logger.info('录制中...')
# print('cmd:\n%s' % cmd)
start_time = datetime.now()
self.process=subprocess.Popen(cmd, shell=shell, universal_newlines = True, stdin = subprocess.PIPE, stderr = subprocess.STDOUT, stdout = subprocess.PIPE)
line = ''
while self.recording:
2018-04-17 08:11:26 +00:00
2018-04-18 02:13:29 +00:00
line += self.process.stdout.readline()
now = datetime.now()
if (now - start_time).total_seconds() >2:
# self.logger.info('recording...')
ffmpeg_running = False
if self.process:
ffmpeg_running = self.process.poll() is None
log_text = 'ffmpeg 运行状态:%s' % ('运行中' if ffmpeg_running else '终止')
print(log_text)
self.logger.info(log_text)
else:
txt= 'ffmpeg子进程已终止.'
print(txt)
self.logger.warning(txt)
if not ffmpeg_running:
raise CalledProcessError(self.process.returncode, cmd)
# self.logger.info(line)
print(line)
line = ''
start_time = now
# print(line)
2018-04-17 08:11:26 +00:00
2018-04-18 02:13:29 +00:00
if not self.recording:
self.process.stdin.write('q')
print(self.process.communicate())
break
except CalledProcessError as e:
log_txt = 'ffmpeg异常终止:\nreturn code: %d\ncmd:\n%s' % (e.returncode, e.cmd)
print(log_txt)
self.logger.warning(log_txt)
self.recording = False
2018-04-17 08:11:26 +00:00
# self.logger.info(self.process.communicate())
# print('done.')
def record(self,cmd='ffmpeg -h', target = None):
if target:
print('cmd: \n%s' % cmd)
self.logger.info('record cmd:\n %s' % cmd)
2018-04-18 02:13:29 +00:00
th=Thread(name=self.record_thread_name, target= target, args = (cmd,), daemon=False)
2018-04-17 08:11:26 +00:00
th.start()
2018-04-18 02:13:29 +00:00
self.recording=True
2018-04-17 08:11:26 +00:00
self.record_thread=th
print('record thread,ident:%d' % self.record_thread.ident)
2018-04-18 02:13:29 +00:00
# th.join()
2018-04-17 08:11:26 +00:00
def stop_record(self):
# print('threading active thread count:%d' % threading.active_count())
try:
self.recording = False
self.logger.info('录制将停止...')
2018-04-18 02:13:29 +00:00
if self.process:
self.logger.info('ffmpeg进程状态: %s' % (self.process.poll() is not None))
print('subprocess return code:%d' % self.process.returncode)
2018-04-17 08:11:26 +00:00
if self.record_thread.is_alive():
self.record_thread.join(1)
print('record thread status: %s' % self.record_thread.is_alive())
except (Exception,KeyboardInterrupt) as e:
print('kill exception:\n %s' % e)
self.logger.warning('kill exception:\n %s' % e)
def record_camera(self):
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' %(
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.get_file_name()
)
# print(record_cmd)
self.record(record_cmd, self.start_ffmpeg)
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(
self.deal_with_device_name(self.screen_name),
self.deal_with_device_name(self.system_voice_device_name),
self.video_codec,
self.resolution,
self.brate,
self.get_file_name()
)
self.record(record_cmd, self.start_ffmpeg)
def debug_camera(self):
try:
play_cmd = ['ffplay','-f','dshow','-i','video={}'.format(self.camera_name),'-window_title','按q退出','-noborder']
self.record(play_cmd, self.play)
except Exception as e:
print(e)
def play(self, cmd):
try:
t_process=subprocess.Popen(cmd, shell= False, universal_newlines = True, stderr = subprocess.STDOUT, stdout = subprocess.PIPE)
while True:
line = t_process.stdout.readline()
print(line)
if line == '':
if t_process.poll() is not None:
break
t_process.communicate()
except (Exception, KeyboardInterrupt) as e:
print(e)
def deal_with_device_name(self,device_name):
# print(device_name)
# new_name=device_name.replace('\\','\\\\')
# print(new_name)
# return new_name
return device_name
def get_file_name(self):
time_str=datetime.now().strftime('%Y%m%d_%H%M%S')
video_type = ''
if self.record_type == RecordType.Camera:
video_type = '摄像头'
if self.record_type == RecordType.Screen:
video_type = '屏幕'
file_name = os.path.join(self.file_dir, '{}_{}{}'.format(video_type, time_str, self.file_suffix))
print('recording file name: %s' % file_name)
return file_name
def _async_raise(self, tid, exctype):
"""raises the exception, performs cleanup if needed"""
tid = ctypes.c_long(tid)
if not inspect.isclass(exctype):
exctype = type(exctype)
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
print('async_raise res value:%d' % res)
if res == 0:
raise ValueError("invalid thread id")
elif res != 1:
# """if it returns a number greater than one, you're in trouble,
# and you should call it again with exc=NULL to revert the effect"""
ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)
raise SystemError("PyThreadState_SetAsyncExc failed")
def kill_process(self, process_name='ffmpeg'):
cmd='tasklist | findstr {}'.format(process_name)
output_strs, output_errs=self.run_cmd(cmd)
pid=[]
if output_strs:
print('find "%s" result: \n%s' % (process_name, ''.join(output_strs)))
for output_str in output_strs:
find_re=re.search(r'({}.+?)\s*([0-9]+)'.format(process_name),output_str)
if find_re:
full_process_name=find_re.group(1).strip()
pid=find_re.group(2).strip()
print('计划结束任务:{}@pid {}'.format( full_process_name, pid ))
task_kill_cmd = 'taskkill /T /F /pid {}'.format(pid)
# status, output = subprocess.getstatusoutput(task_kill_cmd)
# if status == 1:
# print('任务成功被结束:')
# else:
# print('任务结束失败:')
# print(output)
else:
print('not found task about "%s" in tasklist' % process_name )
# def stop_thread(self,thread):
# self._async_raise(thread.ident, SystemExit)