How to make screen recorder in PyQt5

 · 14 mins read

Alright friends welcome back, lets make a very flexible screen recorder today. The target platform for this GUI is Windows 10. For direct installation to windows 10, please download these three parts in a folder and then extract (using winRAR) the PyShine Recorder setup.zip file:

  1. PyShine Recorder setup.zip

  2. PyShine Recorder setup.z02

  3. PyShine Recorder setup.z01

Everything Is AWESOME

IMPORTANT

Please run the code below only from PowerShell and not use any IDE, such as PyCharm or others, because it will not work in them properly. Copy the code below in a Python file as main.py and save it to a location. Then go to that location open up the Windows PowerShell as Shift+ Right click “open PowerShell window here” then run it as:

python3 main.py

main.py


"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
# Subscribe to pyshine youtube channel for more learning videos.
# Its an Audio Video Screen Recoder in Python 3.
# -*- coding: utf-8 -*-
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 

from PyQt5 import QtCore, QtGui, QtWidgets # pip install pyqt5
from PyQt5.QtWidgets import QFileDialog
import os 
import pyautogui # pip install pyautogui
import cv2 
import numpy as np 
import pygetwindow as gw
import _thread
import imutils			
import time
import signal
from threading import Thread 
import shlex
import psutil
import subprocess
from subprocess import Popen
from dateutil.relativedelta import relativedelta  # Install it via: pip install python-dateutil
try: 
	os.remove("output_video.mp4")
except:
	pass

drawing = False
global x1, y1, x2,y2,num,h,w,windowRegion
x1,y1,x2,y2,h,w = 0,0,0,0,0,0
windowRegion=0
num = 0




class Ui_MainWindow(object):
	def setupUi(self, MainWindow):
		self.threadpool = QtCore.QThreadPool()
		MainWindow.setObjectName("MainWindow")
		
		
		
		sizeObject = QtWidgets.QDesktopWidget().screenGeometry(-1)
		

		H = (sizeObject.height())
		W = (sizeObject.width())
		
		self.W = W
		self.H = H
		MainWindow.resize(W//8, H//10)
		MainWindow.setMinimumSize(QtCore.QSize(W//5, H//7))
		MainWindow.setAcceptDrops(True)
		self.centralwidget = QtWidgets.QWidget(MainWindow)
		self.centralwidget.setObjectName("centralwidget")
		self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
		self.gridLayout.setObjectName("gridLayout")
		self.comboBox = QtWidgets.QComboBox(self.centralwidget)
		self.comboBox.setObjectName("comboBox")
		self.comboBox.addItem('')
		self.cmd = ""
		""" AUDIO and VIDIO DEVICES """
		from PyQt5.QtMultimedia import QAudioDeviceInfo,QAudio,QCameraInfo
		# List of Audio Input Devices
		input_audio_deviceInfos = QAudioDeviceInfo.availableDevices(QAudio.AudioInput)
		for device in input_audio_deviceInfos:
			self.comboBox.addItem(device.deviceName())
		""" ----------------------  """
		self.comboBox.setCurrentIndex(1)
		self.Mic = input_audio_deviceInfos[0].deviceName()
		self.gridLayout.addWidget(self.comboBox, 1, 0, 1, 1)
		self.radioButton = QtWidgets.QRadioButton(self.centralwidget)
		self.radioButton.setObjectName("radioButton")
		self.gridLayout.addWidget(self.radioButton, 2, 0, 1, 1)
		self.pushButton = QtWidgets.QPushButton(self.centralwidget)
		self.pushButton.setMinimumSize(QtCore.QSize(0, 23))
		self.pushButton.setIconSize(QtCore.QSize(16, 25))
		self.pushButton.setObjectName("pushButton")
		self.gridLayout.addWidget(self.pushButton, 3, 0, 1, 1)
		MainWindow.setCentralWidget(self.centralwidget)
		self.statusbar = QtWidgets.QStatusBar(MainWindow)
		self.statusbar.setObjectName("statusbar")
		MainWindow.setStatusBar(self.statusbar)
		self.actionNew = QtWidgets.QAction(MainWindow)
		self.actionNew.setObjectName("actionNew")
		self.retranslateUi(MainWindow)
		QtCore.QMetaObject.connectSlotsByName(MainWindow)
		self.clicked = False
		self.pushButton.clicked.connect(self.takeSnapNow)
		self.radioButton.toggled.connect(self.setStatus)
		self.comboBox.currentIndexChanged[str].connect(self.setAudioDevice)
		self.th={}
		self.cap = ""
		self.useCam = False
		self.st = 0
		self.arguments = ''
		self.process = None
		
	
	def setAudioDevice(self,audioD):
		self.Mic = audioD
		
	def setStatus(self):
		if self.useCam ==False:
			self.useCam = True	
		else:
			self.useCam = False
		
	def draw_rect(self,event, x, y, flags, param):
		global x1, y1, drawing,  num, img, img2,x2,y2
		if event == cv2.EVENT_LBUTTONDOWN:
			drawing = True
			x1, y1 = x, y
		elif event == cv2.EVENT_MOUSEMOVE:
			if drawing == True:
				a, b = x, y
				if a != x & b != y:
					img = img2.copy()
				   
					cv2.rectangle(img, (x1,y1),(x,y), (0, 255, 0), 2)
		elif event == cv2.EVENT_LBUTTONUP:
			drawing = False
			num += 1
			font = cv2.FONT_HERSHEY_SIMPLEX
			x2 = x
			y2= y

	def takeSnap(self):
		global x1, y1, drawing,  num, img, img2,x2,y2,h,w
		global windowRegion
		if self.useCam==False:
			key=ord('a')

			im1 = pyautogui.screenshot()
			im1.save(r"monitor-1.png")
			
			img= cv2.imread('monitor-1.png') # reading image
			try:
				os.remove('monitor-1.png')
			except:
				pass
			cv2.putText(img,"Click and select the Region, Press w to confirm selection ",(self.W//8,self.H//2),cv2.FONT_HERSHEY_TRIPLEX, 1.3, (20,255,0), 2, cv2.LINE_AA) 
			
			img2=img.copy()
			cv2.namedWindow("main", cv2.WINDOW_NORMAL)	
			cv2.setMouseCallback("main", self.draw_rect)
			cv2.setWindowProperty("main", cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN);

			while key!=ord('w'):
				cv2.imshow("main",img)
				key = cv2.waitKey(1)&0xFF 
			(h, w) = ((y2-y1),(x2-x1))
			if key==ord('w'):
				cv2.destroyAllWindows()
				
		else:
			x1,y1,w,h = ( 0, 0, self.W, self.H )
		if w%2 ==0:
			pass
		else:
			w=w+1
		if h%2 ==0:
			pass
		else:
			h=h+1
		windowRegion = ( x1, y1, w, h )
		return x1,y1,w,h
	

	def run(self,inp,out):
		global windowRegion
		self.st = time.time()
		cnt = 0
		while (True):
			if self.useCam ==True:
				
				windowRegion = ( 0, 0, self.W, self.H )
			frame = np.array(pyautogui.screenshot(region=windowRegion),dtype="uint8")
			frame = imutils.resize(frame, width=480)
			frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
			cv2.imshow("Preview",frame)
			rt = relativedelta(seconds=time.time()-self.st)
			st = ('{:02d}:{:02d}:{:02d}'.format(int(rt.hours), int(rt.minutes), int(rt.seconds)))
			self.pushButton.setText('Stop Recording: '+st)
			if cv2.waitKey(1) == 27:
				w = gw.getWindowsWithTitle('Windows PowerShell')[0]
				w.close()
				cv2.destroyAllWindows()
				break
		
		
	
	def takeSnapNow(self):
		
		if self.clicked ==False:
			
			filename = QtWidgets.QFileDialog.getSaveFileName( self.centralwidget,"Save video to file", os.sep.join((os.path.expanduser('~'), 'Desktop')), "MP4(*.mp4);;AVI(*.avi);;MKV(*.mkv);;MOV(*.mov)") 
			
			while filename[0] == '': 
				pyautogui.alert(text='Please write a video name', title='File name required', button='OK')	
				filename = QtWidgets.QFileDialog.getSaveFileName( self.centralwidget,"Save video to file", os.sep.join((os.path.expanduser('~'), 'Desktop')), "MP4(*.mp4);;AVI(*.avi);;MKV(*.mkv);;MOV(*.mov)") 
			
			try: 
				os.remove(filename[0])
			except:
				pass
			
			x1,y1,w,h = self.takeSnap()
			
			# self.cmd = r'"ffmpeg -rtbufsize 100M -f dshow -i audio="{}" -f -y -rtbufsize 100M -f gdigrab -framerate 30 -offset_x {} -offset_y {} -video_size {}x{} -draw_mouse 1 -i desktop -c:v libx264 -r 30 -preset ultrafast -tune zerolatency -crf 25 -pix_fmt yuv420p "{}"'.format(self.Mic,x1,y1,w,h,filename[0])
			
			self.cmd = """ffmpeg -rtbufsize 1500M -f dshow -i audio="{}" -f -y -rtbufsize 1500M -f gdigrab -framerate ntsc -offset_x {} -offset_y {} -video_size {}x{} -draw_mouse 1 -i desktop -c:v libx264 -r 30 -preset ultrafast -tune zerolatency -crf 1 -pix_fmt yuv420p "{}" """.format(self.Mic,x1,y1,w,h,filename[0])
			
			
			
			self.arguments = shlex.split(self.cmd)
			
				
			self.th[0] = Thread(target = self.run,args = (1,1))
			self.th[1] = Thread(target = self.makeVideo,args = (1,))
			self.th[0].start()
			self.th[1].start()
			self.clicked =True
			
			
			self.pushButton.setShortcut("Ctrl+r")
			self.radioButton.setEnabled(False)
		else:
			self.pushButton.setEnabled(False)
			
			
			self.pushButton.setText('Finalizing...')

			w = gw.getWindowsWithTitle('Windows PowerShell')[0]
			w.close()

			self.clicked =False
			
	
		
		
	def makeVideo(self,inp):
		
		#self.process = subprocess.Popen(self.arguments, shell=True) 
		FNULL = open(os.devnull, 'w')
		retcode = subprocess.call(self.arguments, stdout=FNULL, stderr=subprocess.STDOUT)
		
		
		
			
		
		
	def retranslateUi(self, MainWindow):
		_translate = QtCore.QCoreApplication.translate
		MainWindow.setWindowTitle(_translate("MainWindow", "PyShine Video Recorder"))
		self.comboBox.setItemText(0, _translate("MainWindow", "Select Audio Device"))
		self.radioButton.setText(_translate("MainWindow", "Full Screen"))
		self.pushButton.setText(_translate("MainWindow", "Start Recording"))
		self.pushButton.setShortcut(_translate("MainWindow", "Ctrl+r"))
		self.actionNew.setText(_translate("MainWindow", "New"))


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())


"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
# Subscribe to pyshine youtube channel for more learning videos.
# Its an Audio Video Screen Recoder in Python 3.
# -*- coding: utf-8 -*-
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""