Video Player with Pyglet with Pyside/PyQt - pyqt4

I would like to make a video player using Pyglet and Pyside.
(I can't use phonon because it requires the K-Lite Codec pack. With pyglet I can package avbin.dll)
I can get the audio of my movie to play, but I'm stuck on getting the video to show in the PySide dialog.
I think I need to use the player.get_texture() command, convert that to image data, then redraw the PySide widget background with that image. When trying this I just get a black image.
Here's my code:
from PySide import QtCore, QtGui
import pyglet
import sys
class VideoPlayer(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.resize(600, 400)
self.setStyleSheet("background-color: black")
self.player = pyglet.media.Player()
media = pyglet.media.load('test.avi')
self.player.queue(media)
self.player.play()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
app.setApplicationName("Media Player")
window = VideoPlayer()
window.show()
sys.exit(app.exec_())
Thanks!
EDIT:
Here's how I was trying to get the image from the pyglet player:
tex = self.player.get_texture().get_image_data()
form = tex.format
pitch = tex.width * len(form)
pixels = tex.get_data(form, pitch)
img = QtGui.QImage()
img.loadFromData(pixels)

Here's how I was able to get the image from pyglet and turn it into a QImage()
tex = self.player.get_texture().get_image_data()
form = tex.format
pitch = tex.width * len(form)
pixels = tex.get_data(form, pitch)
img = QtGui.QImage(pixels, tex.width, tex.height, QtGui.QImage.Format_RGB32)
img = img.rgbSwapped()

Related

Display video size on label tkinter

The below code displays video on the label. But, problem is that, it is displayed in a very zoom (large) manner. I want to resize it to display correctly on label. When I use the option image=image.resize(), I get an error
ValueError: cannot resize this array: it does not own its data
import tkinter as tk, threading
import imageio
from PIL import Image, ImageTk
video_name = "e.mp4"
video = imageio.get_reader(video_name)
#video = video.resize(20,20)
def stream(label):
for image in video.iter_data():
frame_image = ImageTk.PhotoImage(Image.fromarray(image))
label.config(image=frame_image)
label.image = frame_image
root = tk.Tk()
my_label = tk.Label(root, width=500,height=500)
my_label.place(x=0,y=0)
thread = threading.Thread(target=stream, args=(my_label,))
thread.daemon = 1
thread.start()
root.mainloop()
You can call resize() on the return image of Image.fromarray(image):
frame_image = ImageTk.PhotoImage(Image.fromarray(image).resize((100,100)))

PyQt5 VideoPlayer: Converting to Object Orientated Code Prevents Playback

I've been looking into how to incorporate a video or livestream into an app. I have found some functional code that works fine:
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import cv2 # OpenCV
import qimage2ndarray # for a memory leak,see gist
import sys # for exiting
# Minimal implementation...
def displayFrame():
ret, frame = cap.read()
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
image = qimage2ndarray.array2qimage(frame)
label.setPixmap(QPixmap.fromImage(image))
app = QApplication([])
window = QWidget()
# OPENCV
cap = cv2.VideoCapture("Vid.mp4")
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 320)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 240)
# timer for getting frames
timer = QTimer()
timer.timeout.connect(displayFrame)
timer.start(60)
label = QLabel('No Camera Feed')
button = QPushButton("Quiter")
button.clicked.connect(sys.exit) # quiter button
layout = QVBoxLayout()
layout.addWidget(button)
layout.addWidget(label)
window.setLayout(layout)
window.show()
app.exec_()
And I am trying to be able to use this in some object orientated code, with the aim of creating a video playback widget to incorporate into other apps:
import cv2
import sys
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import qimage2ndarray # for a memory leak,see gist
import sys # for exiting
# Minimal implementation...
class basicWindow(QMainWindow):
def __init__(self):
super(basicWindow, self).__init__()
# OPENCV
cap = cv2.VideoCapture("Vid.mp4")
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 320)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 240)
# timer for getting frames
timer = QTimer()
timer.timeout.connect(displayFrame)
timer.start(60)
label = QLabel('No Camera Feed')
button = QPushButton("Quiter")
button.clicked.connect(sys.exit) # quiter button
layout = QVBoxLayout()
layout.addWidget(button)
layout.addWidget(label)
widget = QWidget()
widget.setLayout(layout)
self.setCentralWidget(widget)
def displayFrame():
ret, frame = cap.read()
if ret:
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
image = qimage2ndarray.array2qimage(frame)
try:
label.setPixmap(QPixmap.fromImage(image))
except Exception as e:
print(e)
if __name__ == '__main__':
app = QApplication(sys.argv)
windowExample = basicWindow()
windowExample.show()
sys.exit(app.exec_())
I'm new to both OO coding and PyQt5, so any advice on how I'm either misinterpreting how the code works or what Im missing would be great. I have tried already setting label to a global variable, as I wasnt sure the function displayFrame() was aware of what label to change with label.setPixmap, but otherwise Im a little lost.
In your first example it works because label is a global variable, so displayFrame can access it as such.
In the other case, label is only declared in the scope of the __init__ of the basicWindow instance, so displayFrame knows nothing about it.
Make label a member of the instance (self.label = QLabel()), displayFrame a method of the basicWindow class (def displayFrame(self):) and then access the label correctly; note that you also need to make both cap and timer member of the instance (self), otherwise their objects will be immediately "garbage collected" after __init__ returns.
class basicWindow(QMainWindow):
def __init__(self):
super(basicWindow, self).__init__()
# ...
self.cap = cv2.VideoCapture("Vid.mp4")
# ...
self.timer = QTimer()
self.timer.timeout.connect(self.displayFrame)
self.timer.start(60)
self.label = QLabel('No Camera Feed')
# ...
def displayFrame(self):
ret, frame = self.cap.read()
if ret:
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
image = qimage2ndarray.array2qimage(frame)
try:
self.label.setPixmap(QPixmap.fromImage(image))
except Exception as e:
print(e)
Since you're new to OO programming, I suggest you to start by studying how classes and instances and name resolution work in python.

Flickering video for tkinter video

I am trying to make a simple play/pause application in tkinter. Basically I want to show a video and have a play/pause button underneath.
So, after some research I found this suitable post to show a video using tkinter and opencv:
to show video streaming inside frame in tkinter
When using the code, given in the accepted answer to show a video, there is no problem and I don't see any flickering. Here is the code:
# import the necessary packages
from __future__ import print_function
import tkinter as tk
from PIL import ImageTk, Image
import cv2
root = tk.Tk()
# Create a frame
app = tk.Frame(root, bg="white")
app.grid()
# Create a label in the frame
lmain = tk.Label(app)
lmain.grid()
# Capture from camera
cap = cv2.VideoCapture(r'PATH_TO_VIDEO_FILE')
# function for video streaming
frame_number = 0
def video_stream():
global frame_number
cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number)
success, frame = cap.read()
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
img = Image.fromarray(cv2image)
imgtk = ImageTk.PhotoImage(image=img)
lmain.imgtk = imgtk
lmain.configure(image=imgtk)
lmain.after(1, video_stream)
frame_number += 1
video_stream()
root.mainloop()
Now, I slightly altered the code to be able to use the grid manager and add a play button:
# import the necessary packages
from __future__ import print_function
import tkinter as tk
from PIL import ImageTk, Image
import cv2
class PhotoBoothApp:
def __init__(self, path_to_video):
# initialize the root window
self.window = tk.Tk()
self.window.title("Video_Player")
self.videocap = cv2.VideoCapture(path_to_video)
self.frame_number = 0
# Initalize
self.videocap.set(cv2.CAP_PROP_POS_FRAMES, self.frame_number)
success, self.frame = self.videocap.read()
cv2image = cv2.cvtColor(self.frame, cv2.COLOR_BGR2RGBA)
self.img = Image.fromarray(cv2image)
self.imgtk = ImageTk.PhotoImage(image=self.img)
# Show frames
self.picture_label = tk.Label(self.window, image=self.imgtk, relief=tk.RIDGE).grid(row=0, column=0)
self.btn_next_image=tk.Button(self.window, text="Play", width=50, bg ="green",command=self.video_stream).grid(row=1,column=0)
self.window.mainloop()
def video_stream(self):
self.videocap.set(cv2.CAP_PROP_POS_FRAMES, self.frame_number)
sucess, frame = self.videocap.read()
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
img = Image.fromarray(cv2image)
self.imgtk = ImageTk.PhotoImage(image=img)
self.picture_label = tk.Label(self.window, image=self.imgtk, relief=tk.RIDGE).grid(row=0, column=0)
# Update Frame Number to display
self.frame_number = self.frame_number + 1
self.window.after(1, self.video_stream)
ph = PhotoBoothApp(r'PATH_TO_FILE')
The problem is that when I execute the above code, the video flickers as if tkinter need to reload something in-between frames. I have no clue why this happens.
P.S. This post here Flickering video in opencv-tkinter integration did not help me.
You need to make two changes: split your self.picture_label line to create a proper reference to your Label object, and then use self.picure_label.config(...) to change the image.
class PhotoBoothApp:
def __init__(self, path_to_video):
# initialize the root window
...
self.picture_label = tk.Label(self.window, image=self.imgtk, relief=tk.RIDGE)
self.picture_label.grid(row=0, column=0)
...
def video_stream(self):
...
img = Image.fromarray(cv2image)
self.imgtk = ImageTk.PhotoImage(image=img)
self.picture_label.config(image=self.imgtk)
# Update Frame Number to display
self.frame_number = self.frame_number + 1
self.window.after(1, self.video_stream)
ph = PhotoBoothApp(r'PATH_TO_FILE')

Reading frames from a folder and display in QGraphicsView in pyqt4

I am trying to read frame for a folder and display in QGraphics view.
import os
import PyQt4
from PyQt4 import QtCore, QtGui
dir = "frames"
for file in os.listdir(dir):
grview = QtGui.QGraphicsView()
scene = QtGui.QGraphicsScene()
#pixmap = QtGui.QPixmap(os.path.join(dir, file))
pixmap = QtGui.QPixmap(file)
item = QtGui.QGraphicsPixmapItem(pixmap)
self.scene.addItem(item)
grview.setScene()
grview.show()
#self.scene.update()
The folder named "frames" contains jpg files and is in the same folder as the code. But still I am not able to read the frames.
I have also tried general display without graphicsview to check if it works using the code below but it too doesn't work.
import cv2
import os
import PyQt4
from PyQt4 import QtGui,QtCore
import matplotlib.pyplot as plt
from PIL import Image
def load_images_from_folder(folder):
images = []
for filename in os.listdir(folder):
img = cv2.imread(os.path.join(folder,filename))
if img is not None:
images.append(img)
return images
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
img = load_images_from_folder('/frames')
image = Image.open(img)
image.show()
I am not able to understand what am I missing. I want to display the images sequentially in QGraphicsView resembling a video and in some particular frame I have to select the object of interest by draw a rectangle around it which has to hold in the subsequent frames as well. Is the task i am attempting possible? if yes any help is appreciated
Sorry, but I have PyQt5.
Try it:
import os
import sys
from PyQt5 import Qt
#from PyQt4 import QtCore, QtGui
class MainWindow(Qt.QMainWindow):
def __init__(self, parent=None):
Qt.QMainWindow.__init__(self, parent)
self.scene = Qt.QGraphicsScene()
self.grview = Qt.QGraphicsView(self.scene)
self.setCentralWidget(self.grview)
dir = "frames"
self.listFiles = os.listdir(dir)
self.timer = Qt.QTimer(self)
self.n = 0
self.timer.timeout.connect(self.on_timeout)
self.timer.start(1000)
self.setGeometry(700, 150, 300, 300)
self.show()
def on_timeout(self):
if self.n < len(self.listFiles):
self.scene.clear()
file = self.listFiles[self.n]
pixmap = Qt.QPixmap("frames\{}".format(file)) # !!! "frames\{}"
item = Qt.QGraphicsPixmapItem(pixmap)
self.scene.addItem(item)
self.grview.setScene(self.scene) # !!! (self.scene)
self.n += 1
else:
self.timer.stop()
app = Qt.QApplication(sys.argv)
GUI = MainWindow()
sys.exit(app.exec_())

How to insert VLC instance into a QFrame

I'm trying to make a simple video player app by embedding a VLC instance inside a PyQt widget (a QFrame). I found a few examples that go me going, but my code doesn't quite work. When I launch it, it plays "test_video.mp4", but it launches the regular VLC player app in its own, separate window. When I close out of the VLC player window, obviously the video stops, but the audio continues playing until I close my own Qt (PyQt) window.
edit 1: Forgot to mention I am using python-vlc, downloaded via pip.
### video_player.py
import sys
import vlc
from PyQt4 import QtCore, QtGui
from video_player_main_window import Ui_MainWindow
class StartQT4(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.vlc_instance = vlc.Instance("--no-xlib --sout-all")
self.mediaplayer = self.vlc_instance.media_player_new()
self.mediaplayer.set_xwindow(self.ui.video_frame.winId())
print(self.ui.video_frame.winId())
self.media_path = "test_video.mp4"
self.media = self.vlc_instance.media_new(self.media_path)
self.mediaplayer = self.vlc_instance.media_player_new()
self.mediaplayer.set_media(self.media)
self.mediaplayer.play()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = StartQT4()
myapp.show()
sys.exit(app.exec_())
I added a "print(self.ui.video_frame.win())" just for debugging / sanity check to make sure that was a legitimate value. Command line output below. The "X server failure" shows up after I close the VLC window while my PyQt window is still running.
### command line output
106954771
[00007f9c48055168] vdpau_avcodec generic error: Xlib is required for VDPAU
[00007f9c3c003968] xcb_window window error: X server failure
The "video_player_main_window" is the module that QtDesigner (+ pyuic4) generates. "video_frame" is the name of the QFrame object I'm trying to put the VLC instance into. See full code for video_player_main_window.py here: http://pastebin.com/cHpAHZN2
how if like this :
import sys
import vlc
from PyQt4 import QtCore, QtGui
from video_player_main_window import Ui_MainWindow
class StartQT4(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.vlc_instance = vlc.Instance()
self.mediaplayer = self.vlc_instance.media_player_new()
self.mediaplayer.set_hwnd(int(self.frame.winId()))
self.media_path = "test_video.mp4"
self.media = self.vlc_instance.media_new(self.media_path)
self.media.get_mrl()
self.mediaplayer.set_media(self.media)
self.mediaplayer.play()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = StartQT4()
myapp.show()
sys.exit(app.exec_())
i usualy use this for my simple player.

Resources