pyqt5+opencv实现视频播放
详细源码链接为:https://github.com/2500284064/pyqt5-opencv-video
基本原理是通过 VideoCapture 类读取视频源的每一帧数据,将数据转化为图片展示在QWidget中
(pyqt5网上资料太少,一开始查到用 vlc 做视频控件,走了很多弯路,后面发现 opencv 用来做视频控件更方便,而且opencv 的功能更加全面)
直接贴代码
import time
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from cv2 import *
class VideoBox(QWidget):
VIDEO_TYPE_OFFLINE = 0
VIDEO_TYPE_REAL_TIME = 1
STATUS_INIT = 0
STATUS_PLAYING = 1
STATUS_PAUSE = 2
video_url = ""
def __init__(self, video_url="", video_type=VIDEO_TYPE_OFFLINE, auto_play=False):
QWidget.__init__(self)
self.video_url = video_url
self.video_type = video_type # 0: offline 1: realTime
self.auto_play = auto_play
self.status = self.STATUS_INIT # 0: init 1:playing 2: pause
# 组件展示
self.pictureLabel = QLabel()
init_image = QPixmap("../assets/images/no_video.jpeg").scaled(self.width(), self.height())
self.pictureLabel.setPixmap(init_image)
self.playButton = QPushButton()
self.playButton.setEnabled(True)
self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
self.playButton.clicked.connect(self.switch_video)
control_box = QHBoxLayout()
control_box.setContentsMargins(0, 0, 0, 0)
control_box.addWidget(self.playButton)
layout = QVBoxLayout()
layout.addWidget(self.pictureLabel)
layout.addLayout(control_box)
self.setLayout(layout)
# timer 设置
self.timer = VideoTimer()
self.timer.timeSignal.signal[str].connect(self.show_video_images)
# video 初始设置
self.playCapture = VideoCapture()
if self.video_url != "":
self.playCapture.open(self.video_url)
fps = self.playCapture.get(CAP_PROP_FPS)
self.timer.set_fps(fps)
self.playCapture.release()
if self.auto_play:
self.switch_video()
# self.videoWriter = VideoWriter('*.mp4', VideoWriter_fourcc('M', 'J', 'P', 'G'), self.fps, size)
def reset(self):
self.timer.stop()
self.playCapture.release()
self.status = VideoBox.STATUS_INIT
self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
def show_video_images(self):
if self.playCapture.isOpened():
success, frame = self.playCapture.read()
if success:
height, width = frame.shape[:2]
if frame.ndim == 3:
rgb = cvtColor(frame, COLOR_BGR2RGB)
elif frame.ndim == 2:
rgb = cvtColor(frame, COLOR_GRAY2BGR)
temp_image = QImage(rgb.flatten(), width, height, QImage.Format_RGB888)
temp_pixmap = QPixmap.fromImage(temp_image)
self.pictureLabel.setPixmap(temp_pixmap)
else:
print("read failed, no frame data")
success, frame = self.playCapture.read()
if not success and self.video_type is VideoBox.VIDEO_TYPE_OFFLINE:
print("play finished") # 判断本地文件播放完毕
self.reset()
self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaStop))
return
else:
print("open file or capturing device error, init again")
self.reset()
def switch_video(self):
if self.video_url == "" or self.video_url is None:
return
if self.status is VideoBox.STATUS_INIT:
self.playCapture.open(self.video_url)
self.timer.start()
self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPause))
elif self.status is VideoBox.STATUS_PLAYING:
self.timer.stop()
if self.video_type is VideoBox.VIDEO_TYPE_REAL_TIME:
self.playCapture.release()
self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
elif self.status is VideoBox.STATUS_PAUSE:
if self.video_type is VideoBox.VIDEO_TYPE_REAL_TIME:
self.playCapture.open(self.video_url)
self.timer.start()
self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPause))
self.status = (VideoBox.STATUS_PLAYING,
VideoBox.STATUS_PAUSE,
VideoBox.STATUS_PLAYING)[self.status]
class Communicate(QObject):
signal = pyqtSignal(str)
class VideoTimer(QThread):
def __init__(self, frequent=20):
QThread.__init__(self)
self.stopped = False
self.frequent = frequent
self.timeSignal = Communicate()
self.mutex = QMutex()
def run(self):
with QMutexLocker(self.mutex):
self.stopped = False
while True:
if self.stopped:
return
self.timeSignal.signal.emit("1")
time.sleep(1 / self.frequent)
def stop(self):
with QMutexLocker(self.mutex):
self.stopped = True
def is_stopped(self):
with QMutexLocker(self.mutex):
return self.stopped
def set_fps(self, fps):
self.frequent = fps
if __name__ == "__main__":
app = QApplication(sys.argv)
box = VideoBox("home.mp4")
box.show()
sys.exit(app.exec_())
通过timer根据帧率不停的触发 show_video_images 方法, 读取到每一帧数据,将其展示在QLabel中。
本代码支持实时流,本地视频 和 远程视频文件,如需要测试实时流的播放,将视频 video_url 替换为:
rtmp://live.hkstv.hk.lxdns.com/live/hks