3

I am trying to display transparent label over video output using PyQt and vlc library. Although I can put my label on video, the part under the label is not visible as if there is no video underneath. By the way, target platform is Windows. Also, I think the problem is setting media player to video frame window id. However, if I remove the line 'self.mediaplayer.set_hwnd(int(self.videoframe.winId()))', the video would be displayed in it's own window not my player window.

My attempt looks like this:

this.

My code is attached below:

import platform
import sys
import vlc

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont


class Player(QtWidgets.QMainWindow):

    def __init__(self):
        super().__init__()
        self.setWindowTitle("Media Player")

        # Create a basic vlc instance
        self.instance = vlc.Instance()

        self.media = None

        # Create an empty vlc media player
        self.mediaplayer = self.instance.media_player_new()

        self.widget = QtWidgets.QWidget(self)
        self.setCentralWidget(self.widget)

        self.lbl = QtWidgets.QLabel()
        font = QFont('Helvetica', 16, QFont.Bold)
        self.lbl.setFont(font)
        self.lbl.setText("This is my\ntransparent\nlabel.")
        self.lbl.setAlignment(Qt.AlignCenter)
        self.lbl.setFixedSize(200,200)

        self.videoframe = QtWidgets.QFrame(frameShape=QtWidgets.QFrame.Box, frameShadow=QtWidgets.QFrame.Raised)

        self.videoframe.setFixedSize(600,600)

        self.mediaplayer.set_hwnd(int(self.videoframe.winId()))

        fileName = "C:\\Users\\...\\test.mp4" #dir of the video
        self.media = self.instance.media_new(fileName)

        # Put the media in the media player
        self.mediaplayer.set_media(self.media)

        # Parse the metadata of the file
        self.media.parse()

        self.vboxlayout = QtWidgets.QGridLayout()
        self.vboxlayout.addWidget(self.videoframe,0,0)
        self.vboxlayout.addWidget(self.lbl,0,0)
        self.widget.setLayout(self.vboxlayout)

        self.mediaplayer.play() 

if __name__ == "__main__":

    app = QtWidgets.QApplication(sys.argv)

    player = Player()
    player.show()

    sys.exit(app.exec_())
  • Your code is a bit confused. You create the "videoframe", then the label, then a layout for the frame that is then set for the main widget, which doesn't make a lot of sense. Should the label be overimposed on the video? Note that, depending on the situation (OS, display driver, etc), the vlc video might not have support for partial transparency of overlapping objects. Have you considered using QMediaPlayer instead? – musicamante Sep 07 '21 at 18:07
  • Unrelated: an rtsp stream could also be audio only, and your problem is with video, no matter from what source, displayed using vlc. The title of a question should clearly summarize the problem without being misleading, so it should not refer to "rtsp stream" but "video" (or "vlc video"). – musicamante Sep 07 '21 at 18:13
  • To clarify, the stream is h264 from an IP camera. I created frame and label objects because I will draw some shapes on label when the video playing under the label. I set the frame to main widget since I want to show video is playing correctly and to show the response I take. It is my first time with vlc. Therefore, I don't know whether vlc supports overlapping objects or not. Actually, my first attempt was using QMediaPlayer. Although I manage to take the video with overlapping label, stream was quite sluggish and quality of stream was unpleasent. – user16739995 Sep 08 '21 at 08:12
  • If there is a way to optimise the stream playing with QMediaPlayer (like "--network-caching" in vlc instance) you can recommend that'd also be great. – user16739995 Sep 08 '21 at 08:12
  • https://stackoverflow.com/questions/30146051/can-not-overlay-a-transparent-qlabel-on-a-qwidget-that-libvlc-uses – mfkl Sep 13 '21 at 05:16
  • @mkfl Thanks for reply, I also tried setting window flags and attributes. Unfortunately, it does not work. I know video is playing but I couldn't see the video under transparent label. – user16739995 Sep 13 '21 at 07:31
  • 1
    I know this has been inactive for a long time, but did you find a solution to the problem? – Ali-Ibrahim Aug 30 '22 at 08:53

1 Answers1

0

I just had the same problem and my solution is: JUST FOUR THICK LABELS THAT DRAW A SQUARE. here is my code:

class MouseTracker(QtCore.QObject):
positionChanged = QtCore.pyqtSignal(QtCore.QEvent)

def __init__(self, widget):
    super().__init__(widget)
    self._widget = widget
    self.widget.setMouseTracking(True)
    self.widget.installEventFilter(self)

@property
def widget(self):
    return self._widget

def eventFilter(self, o, e):
    if e.type() == QtCore.QEvent.MouseButtonPress:
        # print("pressed")
        self.positionChanged.emit(e)
    elif e.type() == QtCore.QEvent.MouseButtonRelease:
        # print("release")
        self.positionChanged.emit(e)
    if o is self.widget and e.type() == QtCore.QEvent.MouseMove:
        self.positionChanged.emit(e)
    return super().eventFilter(o, e)

then the mainclass...

class MainWindow(QMainWindow):
def __init__(self, master=None):
    QMainWindow.__init__(self, master)
    self.setWindowTitle("Media Player")
    # creating a basic vlc instance
    self.instance = vlc.Instance()
    # creating an empty vlc media player
    self.mediaplayer = self.instance.media_player_new()
    self.initUI()
    self.isPaused = False

def initUI(self):
    self.x0 = None
    self.y0 = None
    self.x1 = None
    self.y1 = None
    # Set up the user interface, signals & slots
    self.statusBar().showMessage("Ready")
    self.resize(640, 480)

    self.widget = QWidget(self)
    self.setCentralWidget(self.widget)

    # In this widget, the video will be drawn
    if sys.platform == "darwin":  # for MacOS
        from PyQt5.QtWidgets import QMacCocoaViewContainer
        self.video_label = QMacCocoaViewContainer(0)
    else:
        self.video_label = QLabel()

    tracker = MouseTracker(self.video_label)
    tracker.positionChanged.connect(self.on_positionChanged)

    self.positionslider = QSlider(Qt.Horizontal, self)
    self.positionslider.setToolTip("Position")
    self.positionslider.setMaximum(1000)
    self.positionslider.sliderMoved.connect(self.setPosition)
    #########Buttons#################
    self.height_of_BTNS = 30
    self.playbutton = QPushButton("Play")
    self.playbutton.clicked.connect(self.PlayPause)

    self.stopbutton = QPushButton("Stop")
    self.stopbutton.clicked.connect(self.Stop)

    self.volumeslider = QSlider(Qt.Horizontal, self)
    self.volumeslider.setMaximum(100)
    self.volumeslider.setValue(self.mediaplayer.audio_get_volume())
    self.volumeslider.setToolTip("Volume")

    self.volumeslider.valueChanged.connect(self.setVolume)
    #################################
    self.marker_label_top = QLabel(self)
    self.marker_label_top.hide()
    self.marker_label_top.raise_()
    #################################
    self.marker_label_btm = QLabel(self)
    self.marker_label_btm.hide()
    self.marker_label_btm.raise_()
    #################################
    self.marker_label_r = QLabel(self)
    self.marker_label_r.hide()
    self.marker_label_r.raise_()
    #################################
    self.marker_label_l = QLabel(self)
    self.marker_label_l.hide()
    self.marker_label_l.raise_()

    self.hbuttonbox = QHBoxLayout()
    self.hbuttonbox.addWidget(self.playbutton)
    self.hbuttonbox.addWidget(self.stopbutton)
    self.hbuttonbox.addWidget(self.volumeslider)
    self.hbuttonbox.addStretch(1)

    self.vboxlayout = QVBoxLayout()
    self.vboxlayout.addWidget(self.video_label)
    self.vboxlayout.addWidget(self.positionslider)
    self.vboxlayout.addLayout(self.hbuttonbox)

    self.widget.setLayout(self.vboxlayout)
    ##############################
    self.label_position = QtWidgets.QLabel(self.video_label, alignment=QtCore.Qt.AlignCenter)
    self.label_position.setStyleSheet('background-color: white; border: 1px solid black')
    ##############################
    open = QAction("&Open", self)
    open.triggered.connect(self.OpenFile)
    exit = QAction("&Exit", self)
    exit.triggered.connect(sys.exit)
    menubar = self.menuBar()
    filemenu = menubar.addMenu("&File")
    filemenu.addAction(open)
    filemenu.addSeparator()
    filemenu.addAction(exit)

    self.timer = QTimer(self)
    self.timer.setInterval(200)
    self.timer.timeout.connect(self.updateUI)

    self.timer_1 = QTimer(self)
    self.timer_1.setInterval(10000)
    self.timer_1.timeout.connect(self.updateUI_info)

    # Below functions must be FALSE in order to work mousetracker
    self.mediaplayer.video_set_mouse_input(False)
    self.mediaplayer.video_set_key_input(False)

    self.show()

then the function that draw the square when the mouse button released. It actually draw 4 thick labels above the video_label. IMPORTANT TO SET FALSE the self.mediaplayer.video_set_mouse_input(False) and self.mediaplayer.video_set_key_input(False)

@QtCore.pyqtSlot(QtCore.QEvent)
def on_positionChanged(self, e):

    if e.type() == QtCore.QEvent.MouseButtonPress:
        self.statusBar().showMessage("Ready")
        self.marker_label_top.hide()
        self.marker_label_r.hide()
        self.marker_label_l.hide()
        self.marker_label_btm.hide()
        print("Mouse press")
        self.x0 = e.x()
        self.y0 = e.y()
    elif e.type() == QtCore.QEvent.MouseButtonRelease and self.x0 is not None and self.y0 is not None:
        self.x1 = e.x()
        self.y1 = e.y()
        print("Mouse release")
        if abs(self.x0 - self.x1) > 30 and abs(self.y0 - self.y1) > 30:

            marker_width = self.x1 - self.x0
            marker_height = self.y1 - self.y0
            self.marker_label_top.setStyleSheet("border: 2px solid black;")
            self.marker_label_top.resize(marker_width, 2)
            self.marker_label_top.move(self.x0, self.y0)
            self.marker_label_top.show()

            self.marker_label_l.setStyleSheet("border: 2px solid black;")
            self.marker_label_l.resize(2, marker_height)
            self.marker_label_l.move(self.x0, self.y0)
            self.marker_label_l.show()

            self.marker_label_r.setStyleSheet("border: 2px solid black;")
            self.marker_label_r.resize(2, marker_height)
            self.marker_label_r.move(self.x1, self.y0)
            self.marker_label_r.show()

            self.marker_label_btm.setStyleSheet("border: 2px solid black;")
            self.marker_label_btm.resize(marker_width, 2)
            self.marker_label_btm.move(self.x0, self.y1)
            self.marker_label_btm.show()

            print(f"Values UI MOUSE TRACKER X: {self.x0} ,Y: {self.y0}, X1: {self.x1}, Y1: {self.y1}")
        else:
            self.statusBar().showMessage("Make it bigger")
            # self.worker1.reset_values()
            self.x0 = None
            self.y0 = None
            self.x1 = None
            self.y1 = None

    self.label_position.move(e.x(), e.y())
    self.label_position.setText("(%d, %d)" % (e.x(), e.y()))
    self.label_position.adjustSize()
    self.label_position.show()

and the rest of the code

 def PlayPause(self):
    # Toggle play/pause status
    if self.mediaplayer.is_playing():
        self.mediaplayer.pause()
        self.playbutton.setText("Play")
        self.isPaused = True
    else:
        if self.mediaplayer.play() == -1:
            self.OpenFile()
            return
        self.mediaplayer.play()
        self.playbutton.setText("Pause")
        self.timer.start()
        self.timer_1.start()
        self.isPaused = False

def Stop(self):
    self.mediaplayer.stop()
    self.playbutton.setText("Play")

def OpenFile(self, filename=None):
    if filename == False:
        filename = QFileDialog.getOpenFileName(self, "Open File", os.path.expanduser('~'))[0]
    if not filename:
        return
    # # create the media
    if sys.version < '3':
        filename = vlc.unicode(filename)
    self.media = self.instance.media_new(filename)
    # put the media in the media player
    self.mediaplayer.set_media(self.media)
    # parse the metadata of the file
    self.media.parse()

    if sys.platform.startswith('linux'):  # for Linux using the X Server
        self.mediaplayer.set_xwindow(self.video_label.winId())
    elif sys.platform == "win32":  # for Windows
        self.mediaplayer.set_hwnd(self.video_label.winId())
        print(self.video_label.winId())
    elif sys.platform == "darwin":  # for MacOS
        self.mediaplayer.set_nsobject(int(self.video_label.winId()))
    self.PlayPause()

def setVolume(self, Volume):
    self.mediaplayer.audio_set_volume(Volume)

def setPosition(self, position):
    # setting the position to where the slider was dragged
    self.mediaplayer.set_position(position / 1000.0)

def updateUI(self):
    # updates the user interface
    # setting the slider to the desired position
    self.positionslider.setValue(int(self.mediaplayer.get_position() * 1000))
    # self.mediaplayer.video_take_snapshot(num=0, psz_filepath='a.png', i_width=1920, i_height=1080)

    if not self.mediaplayer.is_playing():
        # no need to call this function if nothing is played
        self.timer.stop()
        if not self.isPaused:
            # after the video finished, the play button stills shows
            # "Pause", not the desired behavior of a media player
            # this will fix it
            self.Stop()

def updateUI_info(self):
    fps = self.mediaplayer.get_fps()
    sec = self.mediaplayer.get_time() / 1000
    frame_no = int(round(sec * fps))
    print(
        f" Frame number Now: {frame_no} \n Frame Rate per Second: {fps} \n Current position in seconds {self.mediaplayer.get_time() / 1000} \n -------------------")

if __name__ == "__main__":
    App = QApplication(sys.argv)
    Root = MainWindow()
    if sys.argv[1:]:
        Root.OpenFile(sys.argv[1])
    sys.exit(App.exec_())

enjoy :)

David Dan
  • 17
  • 5