PySide/python视频播放器问题

PySide/python视频播放器问题,python,qt,numpy,video,pyside,Python,Qt,Numpy,Video,Pyside,我正在尝试使用python编写一个简单的YUV视频播放器。经过一些初步的研究,我认为我可以使用PySide并开始使用它。作为第一步,我在不考虑实时性能的情况下采用了以下方法。 读取YUV缓冲区(420平面)->将YUV图像转换为RGB(32位格式)->调用PySide实用程序进行显示。我的简单程序的基本问题是,即使绘制事件似乎是根据(下面)代码中的计数器发生的,但我只能获得要显示的第一帧,其余帧不显示。如有任何意见需要了解,我将不胜感激 (i) 关于定期在QLabel/QWidget上绘制/重新

我正在尝试使用python编写一个简单的YUV视频播放器。经过一些初步的研究,我认为我可以使用PySide并开始使用它。作为第一步,我在不考虑实时性能的情况下采用了以下方法。 读取YUV缓冲区(420平面)->将YUV图像转换为RGB(32位格式)->调用PySide实用程序进行显示。我的简单程序的基本问题是,即使绘制事件似乎是根据(下面)代码中的计数器发生的,但我只能获得要显示的第一帧,其余帧不显示。如有任何意见需要了解,我将不胜感激 (i) 关于定期在QLabel/QWidget上绘制/重新绘制的任何错误和我方缺乏理解。 (ii)从YUV或RGB源指向基于Python的视频播放器/显示器的任何指针

    #!/usr/bin/python

import sys
from PySide.QtCore import *
from PySide.QtGui import *
import array
import numpy as np

class VideoWin(QWidget):
    def __init__(self, width, height, f_yuv):
        QWidget.__init__(self)
        self.width = width
        self.height = height
        self.f_yuv = f_yuv
        self.setWindowTitle('Video Window')
        self.setGeometry(10, 10, width, height)
        self.display_counter = 0
        self.img = QImage(width, height, QImage.Format_ARGB32)
        #qApp.processEvents()

    def getImageBuf(self):
        return self.img.bits()

    def paintEvent(self, e):
        painter = QPainter(self)
        self.display_counter += 1
        painter.drawImage(QPoint(0, 0), self.img)
    def timerSlot(self):
        print "In timer"
        yuv = array.array('B')
        pix = np.ndarray(shape=(height, width), dtype=np.uint32, buffer=self.getImageBuf())

        for i in range(0,self.height):
            for j in range(0, self.width):
                pix[i, j] = 0

        for k in range (0, 10):
            #qApp.processEvents()
            yuv.fromfile(self.f_yuv, 3*self.width*self.height/2)
            for i in range(0, self.height):
                for j in range(0, self.width):
                    Y_val = yuv[(i*self.width)+j]
                    U_val = yuv[self.width*self.height + ((i/2)*(self.width/2))+(j/2)]
                    V_val = yuv[self.width*self.height + self.width*self.height/4 + ((i/2)*(self.width/2))+(j/2)]
                    C = Y_val - 16
                    D = U_val - 128
                    E = V_val - 128
                    R = (( 298 * C           + 409 * E + 128) >> 8)
                    G = (( 298 * C - 100 * D - 208 * E + 128) >> 8)
                    B = (( 298 * C + 516 * D           + 128) >> 8)
                    if R > 255:
                        R = 255
                    if G > 255:
                        G = 255
                    if B > 255:
                        B = 255

                    assert(int(R) < 256)
                    pix[i, j] = (255 << 24 | ((int(R) % 256 )<< 16) | ((int(G) % 256 ) << 8) | (int(B) % 256))

            self.repaint()
            print "videowin.display_counter = %d" % videowin.display_counter


if __name__ == "__main__":
    try:
        yuv_file_name = sys.argv[1]
        width = int(sys.argv[2])
        height = int(sys.argv[3])
        f_yuv = open(yuv_file_name, "rb")

        videoApp = QApplication(sys.argv)

        videowin = VideoWin(width, height, f_yuv)

        timer = QTimer()
        timer.singleShot(100, videowin.timerSlot)

        videowin.show()
        videoApp.exec_()


        sys.exit(0)
    except NameError:
        print("Name Error : ", sys.exc_info()[1])
    except SystemExit:
        print("Closing Window...")
    except Exception:
        print(sys.exc_info()[1])
#/usr/bin/python
导入系统
从PySide.QtCore导入*
从PySide.QtGui导入*
导入数组
将numpy作为np导入
类VideoWin(QWidget):
定义初始值(自身、宽度、高度、f_yuv):
QWidget.\uuuuu初始化(自)
self.width=宽度
自我高度=高度
self.f_yuv=f_yuv
self.setWindowTitle(“视频窗口”)
自设置几何体(10,10,宽度,高度)
self.display\u计数器=0
self.img=QImage(宽度、高度、QImage.Format_ARGB32)
#qApp.processEvents()
def getImageBuf(自身):
返回self.img.bits()
def喷漆事件(自身,e):
油漆工=油漆工(自身)
自显示计数器+=1
painter.drawImage(QPoint(0,0),self.img)
def timerSlot(自):
打印“在计时器中”
yuv=array.array('B')
pix=np.ndarray(shape=(高度、宽度),dtype=np.uint32,buffer=self.getImageBuf())
对于范围内的i(0,自身高度):
对于范围内的j(0,自身宽度):
pix[i,j]=0
对于范围(0,10)内的k:
#qApp.processEvents()
yuv.fromfile(self.f_yuv,3*self.width*self.height/2)
对于范围内的i(0,自身高度):
对于范围内的j(0,自身宽度):
Y_val=yuv[(i*自身宽度)+j]
U_val=yuv[self.width*self.height+((i/2)*(self.width/2))+(j/2)]
V_val=yuv[self.width*self.height+self.width*self.height/4+((i/2)*(self.width/2))+(j/2)]
C=Y_val-16
D=U_val-128
E=V_val-128
R=((298*C+409*E+128)>>8)
G=((298*C-100*D-208*E+128)>>8)
B=((298*C+516*D+128)>>8)
如果R>255:
R=255
如果G>255:
G=255
如果B>255:
B=255
断言(int(R)<256)
pix[i,j]=(255个QPainter.drawImage(…)
虽然捕获信号(获取图像)的插槽显示它被调用的次数与YUV->RGB转换器/解码器发送信号的次数相同,但它也仅显示第一个图像。我还尝试在不同的线程中运行YUV->RGB转换器和视频显示(调用drawImage),但结果是相同的

请注意,在这两种情况下,我都将RGB像素值直接写入QImage对象的位缓冲区,该对象是所示代码中VideoWin类的一部分(注意:代码行pix=np.ndarray(shape=(height,width),dtype=np.uint32,buffer=VideoWin.getImageBuf(),它获取QImage类的img.bits()缓冲区) 此外,对于这个测试,我只解码和显示视频文件的前10帧。 版本:Python-2.7,Qt-4.8.5,使用Pyside

从文件对象f中读取n个项目(作为机器值),并将它们附加到数组的末尾

示例代码未在数组中包含偏移量,因此会反复读取第一帧。一个简单的修复方法是在读取下一帧之前清除数组:

    for k in range (0, 100):
        del yuv[:]
        yuv.fromfile(self.f_yuv, 3*self.width*self.height/2)
请注意,若要查看差异,您需要读取链接到的测试文件的至少60帧,因为前50帧左右都是相同的(即纯绿色背景)。

来自以下文档:

从文件对象f中读取n个项目(作为机器值),并将它们附加到数组的末尾

示例代码未在数组中包含偏移量,因此会反复读取第一帧。一个简单的修复方法是在读取下一帧之前清除数组:

    for k in range (0, 100):
        del yuv[:]
        yuv.fromfile(self.f_yuv, 3*self.width*self.height/2)

请注意,要查看差异,您需要读取链接到的测试文件的至少60帧,因为前50帧左右都是相同的(即纯绿色背景)。

我根据中建议的程序的一些修改(和扩展)完成了这项工作 . 我在处理和显示之间添加了一个双缓冲机制,使用一个数组读取YUV文件,最后作为一个单独的线程运行Yuv2Rgb转换。 以下是任何建议和改进的计划。 谢谢你的指点! 请注意,这不是实时运行的

#!/usr/bin/python

import sys
import time
from threading import Thread
from PySide.QtCore import *
from PySide.QtGui import *
from PIL import Image
import array
import struct
import numpy as np


class VideoDisplay(QLabel):
    def __init__(self):
        super(VideoDisplay, self).__init__()
        self.disp_counter = 0

    def updateFrame(self, image):
        self.disp_counter += 1
        self.setPixmap(QPixmap.fromImage(image))


class YuvVideoPlayer(QWidget):
    video_signal = Signal(QImage)
    video_display = None

    def __init__(self, f_yuv, width, height):
        super(YuvVideoPlayer, self).__init__()
        print "Setting up YuvVideoPlayer params"
        self.img = {}
        self.img[0] = QImage(width, height, QImage.Format_ARGB32)
        self.img[1] = QImage(width, height, QImage.Format_ARGB32)
        self.video_display = VideoDisplay()
        self.video_signal.connect(self.video_display.updateFrame)
        grid = QGridLayout()
        grid.setSpacing(10)
        grid.addWidget(self.video_display, 0, 0)
        self.setLayout(grid)
        self.setGeometry(0, 0, width, height)
        self.setMinimumSize(width, height)
        self.setMaximumSize(width, height)
        self.setWindowTitle('Control Center')
        print "Creating display thread"
        thYuv2Rgb = Thread(target=self.Yuv2Rgb, args=(f_yuv, width, height))
        print "Starting display thread"
        thYuv2Rgb.start()
        self.show()


    def Yuv2Rgb(self, f_yuv, width, height):
        '''This function gets called by an external thread'''
        try:
            yuv = array.array('B')
            pix = {}
            pix[0] = np.ndarray(shape=(height, width), dtype=np.uint32, buffer=self.img[0].bits())
            pix[1] = np.ndarray(shape=(height, width), dtype=np.uint32, buffer=self.img[1].bits())
            for i in range(0,height):
                for j in range(0, width):
                    pix[0][i, j] = 0
                    pix[1][i, j] = 0

            for k in range (0, 10):
                yuv.fromfile(f_yuv, 3*width*height/2)
                #y = yuv[0:width*height]
                for i in range(0, height):
                    for j in range(0, width):
                        Y_val = yuv[(i*width)+j]
                        U_val = yuv[width*height + ((i/2)*(width/2))+(j/2)]
                        V_val = yuv[width*height + width*height/4 + ((i/2)*(width/2))+(j/2)]

                        C = Y_val - 16
                        D = U_val - 128
                        E = V_val - 128
                        R = (( 298 * C           + 409 * E + 128) >> 8)
                        G = (( 298 * C - 100 * D - 208 * E + 128) >> 8)
                        B = (( 298 * C + 516 * D           + 128) >> 8)
                        if R > 255:
                            R = 255
                        if G > 255:
                            G = 255
                        if B > 255:
                            B = 255

                        pix[k % 2][i, j] = (255 << 24 | ((int(R) % 256 )<< 16) | ((int(G) % 256 ) << 8) | (int(B) % 256))
                self.video_signal.emit(self.img[k % 2])
                print "Complted pic num %r, disp_counter = %r" % (k, self.video_display.disp_counter)
                del yuv[:]


        except Exception, e:
            print(e)

if __name__ == "__main__":
    print "In Main"
    yuv_file_name = sys.argv[1]
    width = int(sys.argv[2])
    height = int(sys.argv[3])
    f_yuv = open(yuv_file_name, "rb")

    app = QApplication(sys.argv)
    print "Creating YuvVideoPlayer object"
    ex = YuvVideoPlayer(f_yuv, width, height)
    #ex.up_Video_callback(f_yuv, width, height)
    app.exec_()

    sys.exit(0)
!/usr/bin/python
导入系统
导入时间
从线程导入线程
从PySide.QtCore导入*
从PySide.QtGui导入*
从PIL导入图像
导入数组
导入结构
将numpy作为np导入
类视频显示(QLabel):
定义初始化(自):
超级(视频显示,自我)。\uuu初始化
self.disp_计数器=0
def updateFrame(自我,图像):
self.disp_计数器+=1
self.setPixmap(QPixmap.fromImage(image))
类YuvVideoPlayer(QWidget):
视频信号=信号(QImage)
视频显示=无
定义初始值(自身、f_yuv、宽度、高度):
超级(