赞
踩
在 “PyQt5 实现视频播放器(二) ,详细版本 ,适合新手入门“
中已经想写的介绍了如何使用pyQT 自带的一些控件,实现简单的视频播放功能(例如播放、暂停、进度条跳转、声音控制、全屏播放等),通过评论区的交流,发现大家除了这些简单功能外,还有一个比较强烈的需求就是视频截图,所以下面就来实现一下视频截图功能
在方法调研的前分享一下自己在遇到一个新问题的时候的“搜索方法”,希望能够起到抛砖引玉的作用。(有了正确的搜索方式,对于开发能少走弯路,提高效率)
采用 QMediaPlayer + QAbstractVideoSurface 这个方案重点需要了解一下QAbstractVideoSurface 这个类,结合查询到的资料以及Assistant中的说明:QAbstractVideoSurface class is a base class for video presentation surfaces.
其中 [pure virtual] 的有两个函数:
(1)supportedPixelFormats() # 支持的视频解码后的数据格式
(2)present(const QVideoFrame &frame) # 获取视频解码的数据frame,进行展示
因此我们只需要新写一个类,继承这个抽象的QAbstractVideoSurface 类,然后重写里面的这个两个纯虚函方法,就能从present 输入的Frame中获取每一帧的数据。
如下:
from PyQt5.QtMultimedia import QAbstractVideoSurface, QVideoFrame, QAbstractVideoBuffer from PyQt5.QtCore import pyqtSignal, QDateTime from PyQt5.QtGui import QImage class myVideoSurface(QAbstractVideoSurface): FinishGrab = pyqtSignal() # 截图完成信号 def __init__(self, parent=None): super(QAbstractVideoSurface, self).__init__(parent) def supportedPixelFormats(self, type=None): support_format = [ QVideoFrame.Format_ARGB32, QVideoFrame.Format_ARGB32_Premultiplied, QVideoFrame.Format_ARGB8565_Premultiplied, QVideoFrame.Format_AYUV444, QVideoFrame.Format_AYUV444_Premultiplied, QVideoFrame.Format_BGR24, QVideoFrame.Format_BGR32, QVideoFrame.Format_BGR555, QVideoFrame.Format_BGR565, QVideoFrame.Format_BGRA32, QVideoFrame.Format_BGRA32_Premultiplied, QVideoFrame.Format_BGRA5658_Premultiplied, QVideoFrame.Format_CameraRaw, QVideoFrame.Format_IMC1, QVideoFrame.Format_IMC2, QVideoFrame.Format_IMC3, QVideoFrame.Format_IMC4, QVideoFrame.Format_Jpeg, QVideoFrame.Format_NV12, QVideoFrame.Format_NV21, QVideoFrame.Format_RGB24, QVideoFrame.Format_RGB32, QVideoFrame.Format_RGB555, QVideoFrame.Format_RGB565, QVideoFrame.Format_User, QVideoFrame.Format_UYVY, QVideoFrame.Format_Y16, QVideoFrame.Format_Y8 , QVideoFrame.Format_YUV420P, QVideoFrame.Format_YUV444, QVideoFrame.Format_YUYV, QVideoFrame.Format_YV12, ] return support_format def present(self, frame: 'QVideoFrame'): print("width:{},heigth:{},format:{},start_time:{},endtime{}".format( frame.width(), frame.height(), frame.pixelFormat(), frame.startTime(), frame.endTime() )) if frame.isValid(): frame.map(QAbstractVideoBuffer.ReadOnly) img = QImage(frame.bits(), frame.width(), frame.height(), QVideoFrame.imageFormatFromPixelFormat(frame .pixelFormat())) grab_jpg = './'+QDateTime.currentDateTime().toString("yyyy-MM-dd hh-mm-ss-zzz")+'.jpg' save_state = img.save(grab_jpg) print("截图状态:"+str(save_state)) frame.unmap() self.FinishGrab.emit() return True else: return False
其中 supportedPixelFormats 中的支持的格式只要后面present中能够支持即可,由于我们并不是真的展示,在present中只是转成了QImage,然后完成截图。在这里有两种方案:
(1)使用present中每一帧数据进行展示(这里已经转成QImage图像数据了,接着想怎么画图都可以,然后图像展示就可以),这个方案的话就是图像转换比较耗时,可能会影响到播放的流畅度,如果不是每一帧图像都需要处理的话,不建议用这种方式
(2)播放然后采用“PyQt5 实现视频播放器(二) ,详细版本 ,适合新手入门“
中介绍的widget进行播放,只有需要截图的将player的输出切换到videosurface进行截图,截完图在切换回去,这种方式播放就是在截图的时候由于切换了player的输出因此会导致截图的时候视频界面会就“黑“一下(此时没有输出),
这里考虑到截图功能不是每一帧都需要截(由界面上得按钮触发),因此采用了方案(2)
首先在原先 “PyQt5 实现视频播放器(二) ,详细版本 ,适合新手入门“中的界面基础上增加了一个截图按钮:
在视频播放的时候,点击截图按钮,会自动截取当前时刻的图片,由于只是demo(抛砖引玉的作用),因此图片会保存到demo所在的当前目录下面(保存的代码在present函数中):
某张具体的截图如下:(获取到720x480的原始尺寸大小)
(1)基本完成视频截图的功能,如果需要复杂的功能可以在这个上面继续扩展(例如截图的列表展示、截图的帧号等)
(2)截图的时候已经能够获取每一帧的数据,那么对应的“画框”、图像检测、分类等等都可以在这个上面进行扩展
(3)完整的演示demo 已经打包上传到csdn上:
[https://download.csdn.net/download/u012552296/84553765](demo中有视频和解码器,可以完整运行完整个demo,windows10 环境验证正常)(https://download.csdn.net/download/u012552296/84553765)
[https://download.csdn.net/download/u012552296/16184221] (https://download.csdn.net/download/u012552296/16184221)
如果你想加载一篇你写过的.md文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。
[1]How to save a frame using QMediaPlayer?
https://stackoverflow.com/questions/37724602/how-to-save-a-frame-using-qmediaplayer
[1]原来Qt从视频中获取每一帧数据如此简单: https://blog.csdn.net/jxbinwd/article/details/81034339
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。