pyQT 视频播放器(三) 实现视频截图、获取每一帧数据

3 篇文章 0 订阅

pyQT 视频播放器(三) 实现视频截图、获取每一帧数据

背景

“PyQt5 实现视频播放器(二) ,详细版本 ,适合新手入门“
中已经想写的介绍了如何使用pyQT 自带的一些控件,实现简单的视频播放功能(例如播放、暂停、进度条跳转、声音控制、全屏播放等),通过评论区的交流,发现大家除了这些简单功能外,还有一个比较强烈的需求就是视频截图,所以下面就来实现一下视频截图功能

方法调研

在方法调研的前分享一下自己在遇到一个新问题的时候的“搜索方法”,希望能够起到抛砖引玉的作用。(有了正确的搜索方式,对于开发能少走弯路,提高效率)

  1. 问题定义:这一步也容易也难,特别是工作之后会有更加深刻的体验,简单来说就是如何将你的需求/问题与实际程序实现逻辑进行对应,比如说想实现视频进度条展示,那么对应到具体的pyqt的开发中可能就是,如何使用qt的slider来展示qmediaplayer 播放的媒体进度。这就要求你能对需求/问题进行分解、对使用的程序逻辑/程序组件功能有充分的了解。有点啰嗦了,回归正题,问题定义为:
    qt实现视频截图”、如何获取qt视频播放中的每一帧数据、“qt mediaplayer 截图 ”,
    会用英文更好: get frame data using QMediaPlayer
  2. 搜索: pyQT作为QT的一个python版本接口使用方式,整体的资料虽然不如QT的多,但是QT的使用方式都能够在pyQT上使用,因此我在搜索的时候基本上都是不区分pyqt和QT的,比如这次搜索的时候就是查询qt 如何获取,参考的代码也都是QT的源码,改成python的时候只需要注意import 的位置、以及c++写法到python写法的切换即可。
  3. 筛选:搜索到的方案比较多,大致可以归纳为如下几种:
    (1)使用“截屏”的方式来完成截图,我稍微尝试了一下发现对于qmediaplayer输出的widget无法截取到画面图像,另外即使截取到了也只是UI界面上大小原始原始图像的大小,因此放弃这个方案
    (2)qt + ffmpeg或qt + vlc或qt+ opencv 等进行视频解码播放,功能强大,能够比较方便的进行功能较复杂的图像处理需求,例如使用opencv可以对图像进行复杂的操作(识别、检测、画框)
    (3)QMediaPlayer + QAbstractVideoSurface,比较方便,转换的是QImage,可以获取每一帧数据。
    方案筛选上,由于我们只需要完成截图功能,而且之前用的就是Qmediaplayer,选方案二,能够最快速的实现这个功能。

详细代码说明

采用 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)

参考资料

[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

  • 8
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论
Python是一种流行的编程语言,具有强大的功能和丰富的库,其中PyQt是其形用户界面库之一。我们可以使用Python和PyQt创建一个简单的视频播放器。 首先,我们需要安装PyQt库。可以使用pip命令在命令行中执行以下命令:pip install pyqt5。 接下来,我们需要准备视频文件。假设我们有一个名为video.mp4的视频文件,我们将使用它进行演示。 以下是一个基本的视频播放器示例代码: ``` import sys from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent from PyQt5.QtMultimediaWidgets import QVideoWidget class VideoPlayer(QWidget): def __init__(self): super().__init__() # 创建一个QMediaPlayer对象 self.media_player = QMediaPlayer() # 创建一个QVideoWidget对象作为视频播放区域 self.video_widget = QVideoWidget() # 设置视频播放区域为主窗口的布局 layout = QVBoxLayout() layout.addWidget(self.video_widget) self.setLayout(layout) # 设置视频播放区域作为QMediaPlayer的输出 self.media_player.setVideoOutput(self.video_widget) # 设置要播放的视频文件 video_path = "video.mp4" self.media_player.setMedia(QMediaContent(QUrl.fromLocalFile(video_path))) # 开始播放视频 self.media_player.play() # 创建一个应用程序对象 app = QApplication(sys.argv) # 创建一个视频播放器窗口 video_player = VideoPlayer() video_player.show() # 运行应用程序 sys.exit(app.exec_()) ``` 运行代码后,将会出现一个带有视频播放区域的窗口,并开始播放video.mp4文件。 以上是一个简单的Python PyQt视频播放器示例,你可以根据需求进行进一步的定制和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

皮尔菲特

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值