I am trying to write a simple YUV video player using python. After some initial study, I thought I could use PySide and started with it. As a first step, I have taken the following approach without consideration for real-time performance. Read YUV buffer (420 planar) -> convert the YUV image to RGB (32bit format) - > call PySide utilities for display. The basic problem that I have with my simple program is that I am able to get only the first frame to display and the rest are not displayed, eventhough the paint event seems to be happening according to the counter in the (below) code. I would appreciate any comments to understand (i) any mistakes and lack of understanding from my side regarding painting/repainting at regular intervals on QLabel/QWidget. (ii) Any pointers to Python based video players/display from YUV or RGB source.
#!/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])
I have tried a second approach where I have tried a combination of creating a Signal object which "emits" each decoded RGB image (converted from YUV)as a signal which is caught by the "updateFrame" method in the displaying class which displays the received RGB buffer/frame using QPainter.drawImage(...) method. YUV-to-RGB decode--->Signal(Image buffer) --->updateFrame ---> QPainter.drawImage(...) This also displays only the first image alone although the slot which catches the signal (getting the image) shows that it is called as many times as the signal is sent by the YUV->RGB converter/decoder. I have also tried running the YUV->RGB converter and Video display (calling drawImage) in seperate threads, but the result is the same.
Please note that in both the cases, I am writing the RGB pixel values directly into the bit buffer of the QImage object which is part of the VideoWin class in the code shown (NOTE: the code line pix = np.ndarray(shape=(height, width), dtype=np.uint32, buffer=videowin.getImageBuf()) which gets the img.bits() buffer of the QImage class) Also, for this test I am decoding and displaying only the first 10 frames of the video file. Versions: Python - 2.7, Qt - 4.8.5 using Pyside