python: generate new 3.0.6108 release for bindings master
authorOlivier Aubert <contact@olivieraubert.net>
Sun, 24 Feb 2019 15:23:15 +0000 (16:23 +0100)
committerOlivier Aubert <contact@olivieraubert.net>
Sun, 24 Feb 2019 15:23:15 +0000 (16:23 +0100)
generated/3.0/examples/cocoavlc.py
generated/3.0/examples/pyqt5vlc.py [new file with mode: 0644]
generated/3.0/examples/video_sync/README.md [new file with mode: 0644]
generated/3.0/examples/video_sync/figure.png [new file with mode: 0644]
generated/3.0/examples/video_sync/main.py [new file with mode: 0644]
generated/3.0/examples/video_sync/mini_player.py [new file with mode: 0644]
generated/3.0/examples/video_sync/network.py [new file with mode: 0644]
generated/3.0/setup.py
generated/3.0/vlc.py
generated/dev/vlc.py [changed mode: 0755->0644]

index 9e65fc4..3a9aa50 100755 (executable)
@@ -7,10 +7,10 @@
 # and the corresponding VLC App, see <http://www.VideoLan.org/index.html>.
 
 # This VLC player has only been tested with VLC 2.2.6, 2.2.8, 3.0.1,
-# 3.0.2 and 3.0.3 and the compatible vlc.py Python-VLC binding using
-# 64-bit Python 2.7.14, 2.7.15, 3.6.4, 3.6.5 and/or 3.7.0 on macOS
-# 10.13.4, 10.13.5 and 10.13.6 High Sierra.  This player does not work
-# using PyPy Python <http://PyPy.org> nor with Intel(R) Python
+# 3.0.2 and 3.0.4 and the compatible vlc.py Python-VLC binding using
+# 64-bit Python 2.7.14, 2.7.15, 3.6.4, 3.6.5, 3.7.0, 3.7.1 and/or 3.7.2
+# on macOS 10.13.4, 10.13.5 and 10.13.6 High Sierra.  This player does
+# not work with PyPy Python <http://PyPy.org> nor with Intel(R) Python
 # <http://Software.Intel.com/en-us/distribution-for-python>.
 
 # MIT License <http://OpenSource.org/licenses/MIT>
@@ -67,7 +67,7 @@ except ImportError:
     from urllib.parse import unquote as mrl_unquote  # Python 3+
 
 __all__  = ('AppVLC',)
-__version__ = '18.08.15'
+__version__ = '19.01.31'
 
 _Adjust  = vlc.VideoAdjustOption  # Enum
 # <http://Wiki.VideoLan.org/Documentation:Modules/adjust>
@@ -90,6 +90,11 @@ def _mspf(fps):
     return 1000.0 / (fps or 25)
 
 
+def _ms2str(ms):
+    # convert milliseconds to seconds string
+    return '%.3f s' % (max(ms, 0) * 0.001,)
+
+
 class AppVLC(App):
     '''The application with callback methods for C{app..._},
        C{menu..._} and C{window..._} events.
@@ -202,7 +207,7 @@ class AppVLC(App):
     def menuFilters_(self, item):
         try:
             self.menuPause_(item)
-            # display audio/video filters table
+            # display a table of audio/video filters
             t = Table(' Name:150:bold', ' Short:150:Center:center', ' Long:300', 'Help')
             i = self.player.get_instance()
             b = item.title.split()[0]
@@ -248,9 +253,9 @@ class AppVLC(App):
                 t.append('size', z1000str(z), zSIstr(z))
             t.append('state', str(p.get_state()))
             f = max(p.get_position(), 0)
-            t.append('position', '%.2f%%' % (f * 100,), f)
-            f = ['%.3f s' % (ms * 1e-3) for ms in (p.get_time(), m.get_duration())]
-            t.append('time/duration', *f)  # both shown in seconds
+            t.append('position/length', '%.2f%%' % (f * 100,), _ms2str(p.get_length()))
+            f = map(_ms2str, (p.get_time(), m.get_duration()))
+            t.append('time/duration', *f)
             t.append('track/count', z1000str(p.video_get_track()), z1000str(p.video_get_track_count()))
             t.separator()
 
@@ -294,8 +299,7 @@ class AppVLC(App):
                 if s.input_bitrate > 0:  # XXX approximate caching, based
                     # on <http://GitHub.com/oaubert/python-vlc/issues/61>
                     b = s.read_bytes - s.demux_read_bytes
-                    c = b / (s.input_bitrate * 1000)
-                    t.append('input caching', '%.3f s' % (c,), zSIstr(b))
+                    t.append('input caching', _ms2str(b / s.input_bitrate), zSIstr(b))
                 t.append('demux read',     *zSIstr(s.demux_read_bytes).split())
                 t.append('stream bitrate', *Kops2bpstr2(s.demux_bitrate))
 
diff --git a/generated/3.0/examples/pyqt5vlc.py b/generated/3.0/examples/pyqt5vlc.py
new file mode 100644 (file)
index 0000000..abf0cf2
--- /dev/null
@@ -0,0 +1,205 @@
+"""
+A simple example for VLC python bindings using PyQt5.
+
+Author: Saveliy Yusufov, Columbia University, sy2685@columbia.edu
+Date: 25 December 2018
+"""
+
+import platform
+import os
+import sys
+
+from PyQt5 import QtWidgets, QtGui, QtCore
+import vlc
+
+class Player(QtWidgets.QMainWindow):
+    """A simple Media Player using VLC and Qt
+    """
+
+    def __init__(self, master=None):
+        QtWidgets.QMainWindow.__init__(self, master)
+        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.create_ui()
+        self.is_paused = False
+
+    def create_ui(self):
+        """Set up the user interface, signals & slots
+        """
+        self.widget = QtWidgets.QWidget(self)
+        self.setCentralWidget(self.widget)
+
+        # In this widget, the video will be drawn
+        if platform.system() == "Darwin": # for MacOS
+            self.videoframe = QtWidgets.QMacCocoaViewContainer(0)
+        else:
+            self.videoframe = QtWidgets.QFrame()
+
+        self.palette = self.videoframe.palette()
+        self.palette.setColor(QtGui.QPalette.Window, QtGui.QColor(0, 0, 0))
+        self.videoframe.setPalette(self.palette)
+        self.videoframe.setAutoFillBackground(True)
+
+        self.positionslider = QtWidgets.QSlider(QtCore.Qt.Horizontal, self)
+        self.positionslider.setToolTip("Position")
+        self.positionslider.setMaximum(1000)
+        self.positionslider.sliderMoved.connect(self.set_position)
+        self.positionslider.sliderPressed.connect(self.set_position)
+
+        self.hbuttonbox = QtWidgets.QHBoxLayout()
+        self.playbutton = QtWidgets.QPushButton("Play")
+        self.hbuttonbox.addWidget(self.playbutton)
+        self.playbutton.clicked.connect(self.play_pause)
+
+        self.stopbutton = QtWidgets.QPushButton("Stop")
+        self.hbuttonbox.addWidget(self.stopbutton)
+        self.stopbutton.clicked.connect(self.stop)
+
+        self.hbuttonbox.addStretch(1)
+        self.volumeslider = QtWidgets.QSlider(QtCore.Qt.Horizontal, self)
+        self.volumeslider.setMaximum(100)
+        self.volumeslider.setValue(self.mediaplayer.audio_get_volume())
+        self.volumeslider.setToolTip("Volume")
+        self.hbuttonbox.addWidget(self.volumeslider)
+        self.volumeslider.valueChanged.connect(self.set_volume)
+
+        self.vboxlayout = QtWidgets.QVBoxLayout()
+        self.vboxlayout.addWidget(self.videoframe)
+        self.vboxlayout.addWidget(self.positionslider)
+        self.vboxlayout.addLayout(self.hbuttonbox)
+
+        self.widget.setLayout(self.vboxlayout)
+
+        menu_bar = self.menuBar()
+
+        # File menu
+        file_menu = menu_bar.addMenu("File")
+
+        # Add actions to file menu
+        open_action = QtWidgets.QAction("Load Video", self)
+        close_action = QtWidgets.QAction("Close App", self)
+        file_menu.addAction(open_action)
+        file_menu.addAction(close_action)
+
+        open_action.triggered.connect(self.open_file)
+        close_action.triggered.connect(sys.exit)
+
+        self.timer = QtCore.QTimer(self)
+        self.timer.setInterval(100)
+        self.timer.timeout.connect(self.update_ui)
+
+    def play_pause(self):
+        """Toggle play/pause status
+        """
+        if self.mediaplayer.is_playing():
+            self.mediaplayer.pause()
+            self.playbutton.setText("Play")
+            self.is_paused = True
+            self.timer.stop()
+        else:
+            if self.mediaplayer.play() == -1:
+                self.open_file()
+                return
+
+            self.mediaplayer.play()
+            self.playbutton.setText("Pause")
+            self.timer.start()
+            self.is_paused = False
+
+    def stop(self):
+        """Stop player
+        """
+        self.mediaplayer.stop()
+        self.playbutton.setText("Play")
+
+    def open_file(self):
+        """Open a media file in a MediaPlayer
+        """
+
+        dialog_txt = "Choose Media File"
+        filename = QtWidgets.QFileDialog.getOpenFileName(self, dialog_txt, os.path.expanduser('~'))
+        if not filename:
+            return
+
+        # getOpenFileName returns a tuple, so use only the actual file name
+        self.media = self.instance.media_new(filename[0])
+
+        # Put the media in the media player
+        self.mediaplayer.set_media(self.media)
+
+        # Parse the metadata of the file
+        self.media.parse()
+
+        # Set the title of the track as window title
+        self.setWindowTitle(self.media.get_meta(0))
+
+        # The media player has to be 'connected' to the QFrame (otherwise the
+        # video would be displayed in it's own window). This is platform
+        # specific, so we must give the ID of the QFrame (or similar object) to
+        # vlc. Different platforms have different functions for this
+        if platform.system() == "Linux": # for Linux using the X Server
+            self.mediaplayer.set_xwindow(int(self.videoframe.winId()))
+        elif platform.system() == "Windows": # for Windows
+            self.mediaplayer.set_hwnd(int(self.videoframe.winId()))
+        elif platform.system() == "Darwin": # for MacOS
+            self.mediaplayer.set_nsobject(int(self.videoframe.winId()))
+
+        self.play_pause()
+
+    def set_volume(self, volume):
+        """Set the volume
+        """
+        self.mediaplayer.audio_set_volume(volume)
+
+    def set_position(self):
+        """Set the movie position according to the position slider.
+        """
+
+        # The vlc MediaPlayer needs a float value between 0 and 1, Qt uses
+        # integer variables, so you need a factor; the higher the factor, the
+        # more precise are the results (1000 should suffice).
+
+        # Set the media position to where the slider was dragged
+        self.timer.stop()
+        pos = self.positionslider.value()
+        self.mediaplayer.set_position(pos / 1000.0)
+        self.timer.start()
+
+    def update_ui(self):
+        """Updates the user interface"""
+
+        # Set the slider's position to its corresponding media position
+        # Note that the setValue function only takes values of type int,
+        # so we must first convert the corresponding media position.
+        media_pos = int(self.mediaplayer.get_position() * 1000)
+        self.positionslider.setValue(media_pos)
+
+        # No need to call this function if nothing is played
+        if not self.mediaplayer.is_playing():
+            self.timer.stop()
+
+            # After the video finished, the play button stills shows "Pause",
+            # which is not the desired behavior of a media player.
+            # This fixes that "bug".
+            if not self.is_paused:
+                self.stop()
+
+def main():
+    """Entry point for our simple vlc player
+    """
+    app = QtWidgets.QApplication(sys.argv)
+    player = Player()
+    player.show()
+    player.resize(640, 480)
+    sys.exit(app.exec_())
+
+if __name__ == "__main__":
+    main()
diff --git a/generated/3.0/examples/video_sync/README.md b/generated/3.0/examples/video_sync/README.md
new file mode 100644 (file)
index 0000000..c3792d8
--- /dev/null
@@ -0,0 +1,11 @@
+## Video Synchronization with python-vlc
+
+<img src="figure.png" width="500" align="right">
+
+Each video player is launched as a separate process. `main.py` is the entry point into the program. It creates the "main" video player with all of its respective controls. The main video player is started alongside a multithreaded server that "broadcasts" signals (e.g. play, pause, stop, and etc.) and the current time of the video player, to all client/slave videos. Specifically, this is accomplished by putting the current time or the signal of the main video player into a `queue` (FIFO). The multithreaded server gets the value from the queue and sends the value to each connected client.
+
+In a somewhat similar fashion, each "slave" video player is launched as a separate process, along with a client. The client has a `data_receiver` thread that continuously listens for data, receives data, parses the data, and puts the data into a `queue` that is specific to that "slave" video player. The "slave" video player gets the value from the queue and updates itself in accordance with that value.
+
+In order to facilitate interprocess communication, UNIX domain sockets are used to send and receive data. For the time being, Windows users have to use TCP/IP sockets.
+
+Note: for the sake of clarity, the figure on the right only shows the case of 3 client/slave videos.
diff --git a/generated/3.0/examples/video_sync/figure.png b/generated/3.0/examples/video_sync/figure.png
new file mode 100644 (file)
index 0000000..f0feb1d
Binary files /dev/null and b/generated/3.0/examples/video_sync/figure.png differ
diff --git a/generated/3.0/examples/video_sync/main.py b/generated/3.0/examples/video_sync/main.py
new file mode 100644 (file)
index 0000000..f3ed9c0
--- /dev/null
@@ -0,0 +1,396 @@
+#
+# PyQt5-based video-sync example for VLC Python bindings
+# Copyright (C) 2009-2010 the VideoLAN team
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+#
+"""
+A VLC python bindings player implemented with PyQt5 that is meant to be utilized
+as the "master" player that controls all "slave players".
+
+Author: Saveliy Yusufov, Columbia University, sy2685@columbia.edu
+Date: 25 January 2019
+"""
+
+import platform
+import queue
+import os
+import subprocess
+import sys
+
+from PyQt5 import QtWidgets, QtGui, QtCore
+import vlc
+from network import Server
+
+
+class Player(QtWidgets.QMainWindow):
+    """A "master" Media Player using VLC and Qt
+    """
+
+    def __init__(self, master=None):
+        QtWidgets.QMainWindow.__init__(self, master)
+        self.setWindowTitle("Main 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.create_ui()
+        self.data_queue = queue.Queue()
+        self.is_paused = False
+
+        self.timer = QtCore.QTimer(self)
+        self.timer.setInterval(100)
+        self.timer.timeout.connect(self.update_ui)
+        self.timer.timeout.connect(self.update_time_label)
+
+    def create_ui(self):
+        """Set up the user interface, signals & slots
+        """
+        self.widget = QtWidgets.QWidget(self)
+        self.setCentralWidget(self.widget)
+
+        # In this widget, the video will be drawn
+        if platform.system() == "Darwin":  # for MacOS
+            self.videoframe = QtWidgets.QMacCocoaViewContainer(0)
+        else:
+            self.videoframe = QtWidgets.QFrame()
+
+        self.palette = self.videoframe.palette()
+        self.palette.setColor(QtGui.QPalette.Window, QtGui.QColor(0, 0, 0))
+        self.videoframe.setPalette(self.palette)
+        self.videoframe.setAutoFillBackground(True)
+
+        # Create the time display
+        self.timelabel = QtWidgets.QLabel("00:00:00", self)
+
+        # Create the position slider (QSlider)
+        self.positionslider = QtWidgets.QSlider(QtCore.Qt.Horizontal, self)
+        self.positionslider.setToolTip("Position")
+        self.positionslider.setMaximum(1000)
+        self.positionslider.sliderMoved.connect(self.set_position)
+        # self.positionslider.sliderPressed.connect(self.set_position)
+        self.positionslider.sliderMoved.connect(self.update_time_label)
+
+        # Create the "previous frame" button
+        self.previousframe = QtWidgets.QPushButton()
+        self.previousframe.setFixedWidth(25)
+        self.previousframe.setIcon(self.style().standardIcon(QtWidgets.QStyle.SP_MediaSkipBackward))
+        self.previousframe.clicked.connect(self.on_previous_frame)
+
+        # Create the play button and connect it to the play/pause function
+        self.playbutton = QtWidgets.QPushButton()
+        self.playbutton.setFixedWidth(40)
+        self.playbutton.setIcon(self.style().standardIcon(QtWidgets.QStyle.SP_MediaPlay))
+        self.playbutton.clicked.connect(self.play_pause)
+
+        # Create the "next frame" button
+        self.nextframe = QtWidgets.QPushButton()
+        self.nextframe.setFixedWidth(25)
+        self.nextframe.setIcon(self.style().standardIcon(QtWidgets.QStyle.SP_MediaSkipForward))
+        self.nextframe.clicked.connect(self.on_next_frame)
+
+        # Create the "Playback rate" label
+        self.pb_rate_label = QtWidgets.QLabel("Playback rate: {}x".format(self.mediaplayer.get_rate()), self)
+
+        # Create the "decrease playback rate" button
+        self.decr_pb_rate = QtWidgets.QPushButton()
+        self.decr_pb_rate.setFixedWidth(30)
+        self.decr_pb_rate.setIcon(self.style().standardIcon(QtWidgets.QStyle.SP_MediaSeekBackward))
+        self.decr_pb_rate.clicked.connect(self.decr_mov_play_rate)
+
+        # Create the stop button and connect it to the stop function
+        self.stopbutton = QtWidgets.QPushButton()
+        self.stopbutton.setFixedWidth(30)
+        self.stopbutton.setIcon(self.style().standardIcon(QtWidgets.QStyle.SP_MediaStop))
+        self.stopbutton.clicked.connect(self.stop)
+
+        # Create the "increase playback rate" button
+        self.incr_pb_rate = QtWidgets.QPushButton()
+        self.incr_pb_rate.setFixedWidth(30)
+        self.incr_pb_rate.setIcon(self.style().standardIcon(QtWidgets.QStyle.SP_MediaSeekForward))
+        self.incr_pb_rate.clicked.connect(self.incr_mov_play_rate)
+
+        self.top_control_box = QtWidgets.QHBoxLayout()
+
+        # Add the time and position slider to the 1st controls layout
+        self.top_control_box.addWidget(self.timelabel)
+        self.top_control_box.addWidget(self.positionslider)
+
+        self.bottom_control_box = QtWidgets.QHBoxLayout()
+
+        # Add the buttons to the 2nd controls layout
+        self.bottom_control_box.addWidget(self.previousframe)
+        self.bottom_control_box.addWidget(self.playbutton)
+        self.bottom_control_box.addWidget(self.nextframe)
+        self.bottom_control_box.addWidget(self.pb_rate_label)
+        self.bottom_control_box.addWidget(self.decr_pb_rate)
+        self.bottom_control_box.addWidget(self.stopbutton)
+        self.bottom_control_box.addWidget(self.incr_pb_rate)
+
+        self.vboxlayout = QtWidgets.QVBoxLayout()
+
+        self.vboxlayout.addWidget(self.videoframe)
+        self.vboxlayout.addLayout(self.top_control_box)
+        self.vboxlayout.addLayout(self.bottom_control_box)
+
+        self.widget.setLayout(self.vboxlayout)
+
+        menu_bar = self.menuBar()
+
+        # File menu
+        file_menu = menu_bar.addMenu("File")
+
+        # Create submenu to start new processes from file menu
+        new_menu = QtWidgets.QMenu("Launch", self)
+        file_menu.addMenu(new_menu)
+        new_video_action = QtWidgets.QAction("New Video", self)
+        new_menu.addAction(new_video_action)
+        new_video_action.triggered.connect(on_new_video)
+
+        # Create actions to load a new media file and to close the app
+        open_action = QtWidgets.QAction("Load Video", self)
+        close_action = QtWidgets.QAction("Close App", self)
+        file_menu.addAction(open_action)
+        file_menu.addAction(close_action)
+        open_action.triggered.connect(self.open_file)
+        close_action.triggered.connect(sys.exit)
+
+    def play_pause(self):
+        """Toggle play/pause status
+        """
+        if self.mediaplayer.is_playing():
+            signal = 'p'
+            self.mediaplayer.pause()
+            self.playbutton.setIcon(self.style().standardIcon(QtWidgets.QStyle.SP_MediaPlay))
+            self.is_paused = True
+            self.timer.stop()
+        else:
+            if self.mediaplayer.play() == -1:
+                self.open_file()
+                return
+
+            signal = 'P'
+            self.mediaplayer.play()
+            self.playbutton.setIcon(self.style().standardIcon(QtWidgets.QStyle.SP_MediaPause))
+            self.timer.start()
+            self.is_paused = False
+
+        # Reset the queue & send the appropriate signal, i.e., play/pause
+        self.data_queue.queue.clear()
+        self.data_queue.put('d')
+        self.data_queue.put(signal)
+
+    def stop(self):
+        """Stop player
+        """
+        self.mediaplayer.stop()
+        self.playbutton.setIcon(self.style().standardIcon(QtWidgets.QStyle.SP_MediaPlay))
+
+        # Reset the time label back to 00:00:00
+        time = QtCore.QTime(0, 0, 0, 0)
+        self.timelabel.setText(time.toString())
+
+        # Reset the queue
+        self.data_queue.queue.clear()
+        self.data_queue.put('d')
+        self.data_queue.put('S')
+
+        # Reset the media position slider
+        self.positionslider.setValue(0)
+
+        self.timer.stop()
+
+    def on_next_frame(self):
+        """Go forward one frame.
+
+            The Python VLC binding next_frame function causes a:
+
+            "direct3d11 vout display error: SetThumbNailClip failed"
+
+            error when next_frame is called while the video is playing,
+            so we are using our own fucntion to get the next frame.
+        """
+        # self.mediaplayer.next_frame()
+        next_frame_time = self.mediaplayer.get_time() + self.mspf()
+
+        # Reset the queue & put the next frame's time into the queue
+        self.data_queue.queue.clear()
+        self.data_queue.put('d')
+        self.data_queue.put(next_frame_time)
+        self.update_time_label()
+        self.mediaplayer.set_time(next_frame_time)
+
+    def on_previous_frame(self):
+        """Go backward one frame"""
+        next_frame_time = self.mediaplayer.get_time() - self.mspf()
+
+        # Reset the queue & put the next frame's time into the queue
+        self.data_queue.queue.clear()
+        self.data_queue.put('d')
+        self.data_queue.put(next_frame_time)
+        self.update_time_label()
+        self.mediaplayer.set_time(next_frame_time)
+
+    def mspf(self):
+        """Milliseconds per frame"""
+        return int(1000 // (self.mediaplayer.get_fps() or 25))
+
+    def incr_mov_play_rate(self):
+        """Increase the movie play rate by a factor of 2."""
+        if self.mediaplayer.get_rate() >= 64:
+            return
+
+        rate = self.mediaplayer.get_rate() * 2
+        result = self.mediaplayer.set_rate(rate)
+        if result == 0:
+            self.data_queue.queue.clear()
+            self.data_queue.put('d')
+            self.data_queue.put('>')
+            self.update_pb_rate_label()
+
+    def decr_mov_play_rate(self):
+        """Decrease the movie play rate by a factor of 2."""
+        if self.mediaplayer.get_rate() <= 0.125:
+            return
+
+        rate = self.mediaplayer.get_rate() * 0.5
+        result = self.mediaplayer.set_rate(rate)
+        if result == 0:
+            self.data_queue.queue.clear()
+            self.data_queue.put('d')
+            self.data_queue.put('<')
+            self.update_pb_rate_label()
+
+    def open_file(self):
+        """Open a media file in a MediaPlayer
+        """
+        dialog_txt = "Choose Media File"
+        filename = QtWidgets.QFileDialog.getOpenFileName(self, dialog_txt, os.path.expanduser('~'))
+        if not filename[0]:
+            return
+
+        # getOpenFileName returns a tuple, so use only the actual file name
+        self.media = self.instance.media_new(filename[0])
+
+        # Put the media in the media player
+        self.mediaplayer.set_media(self.media)
+
+        # Parse the metadata of the file
+        self.media.parse()
+
+        # Set the title of the track as window title
+        self.setWindowTitle("Main Media Player: {}".format(self.media.get_meta(0)))
+
+        # The media player has to be 'connected' to the QFrame (otherwise the
+        # video would be displayed in it's own window). This is platform
+        # specific, so we must give the ID of the QFrame (or similar object) to
+        # vlc. Different platforms have different functions for this.
+        if platform.system() == "Linux":  # for Linux using the X Server
+            self.mediaplayer.set_xwindow(int(self.videoframe.winId()))
+        elif platform.system() == "Windows":  # for Windows
+            self.mediaplayer.set_hwnd(int(self.videoframe.winId()))
+        elif platform.system() == "Darwin":  # for MacOS
+            self.mediaplayer.set_nsobject(int(self.videoframe.winId()))
+
+        self.play_pause()
+
+    def set_position(self):
+        """Set the movie position according to the position slider.
+
+        The vlc MediaPlayer needs a float value between 0 and 1, Qt uses
+        integer variables, so you need a factor; the higher the factor,
+        the more precise are the results (1000 should suffice).
+        """
+        # Set the media position to where the slider was dragged
+        self.timer.stop()
+        pos = self.positionslider.value()
+
+        if pos >= 0:
+            self.data_queue.queue.clear()
+            self.data_queue.put('d')
+            current_time = self.mediaplayer.get_time()
+
+            # If the player is stopped, do not attempt to send a -1!!!
+            if current_time == -1:
+                self.timer.start()
+                return
+            self.data_queue.put(current_time)
+
+        self.mediaplayer.set_position(pos * .001)
+        self.timer.start()
+
+    def update_ui(self):
+        """Updates the user interface"""
+
+        # Set the slider's position to its corresponding media position
+        # Note that the setValue function only takes values of type int,
+        # so we must first convert the corresponding media position.
+        media_pos = int(self.mediaplayer.get_position() * 1000)
+        self.positionslider.setValue(media_pos)
+
+        if media_pos >= 0 and self.mediaplayer.is_playing():
+            current_time = self.mediaplayer.get_time()
+            self.data_queue.put(current_time)
+        else:
+            self.data_queue.queue.clear()
+
+        # No need to call this function if nothing is played
+        if not self.mediaplayer.is_playing():
+            self.timer.stop()
+
+            # After the video finished, the play button stills shows "Pause",
+            # which is not the desired behavior of a media player.
+            # This fixes that "bug".
+            if not self.is_paused:
+                self.stop()
+
+    def update_time_label(self):
+        mtime = QtCore.QTime(0, 0, 0, 0)
+        self.time = mtime.addMSecs(self.mediaplayer.get_time())
+        self.timelabel.setText(self.time.toString())
+
+    def update_pb_rate_label(self):
+        self.pb_rate_label.setText("Playback rate: {}x".format(str(self.mediaplayer.get_rate())))
+
+
+def on_new_video():
+    """Launches a new PyQt5-based "mini" media player
+    """
+    if platform.system() == "Windows":
+        subprocess.Popen(["python", "mini_player.py"], shell=True)
+    else:
+        subprocess.Popen(["python", "mini_player.py"])
+
+
+def main():
+    """Entry point for our simple vlc player
+    """
+    app = QtWidgets.QApplication(sys.argv)
+    player = Player()
+
+    _ = Server("localhost", 10000, player.data_queue)
+
+    player.show()
+    player.resize(640, 480)
+    sys.exit(app.exec_())
+
+
+if __name__ == "__main__":
+    main()
diff --git a/generated/3.0/examples/video_sync/mini_player.py b/generated/3.0/examples/video_sync/mini_player.py
new file mode 100644 (file)
index 0000000..a65719d
--- /dev/null
@@ -0,0 +1,169 @@
+#
+# PyQt5-based video-sync example for VLC Python bindings
+# Copyright (C) 2009-2010 the VideoLAN team
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+#
+"""
+This module contains a bare-bones VLC player class to play videos.
+
+Author: Saveliy Yusufov, Columbia University, sy2685@columbia.edu
+Date: 25 January 2019
+"""
+
+import os
+import sys
+import queue
+import platform
+
+from PyQt5 import QtWidgets, QtGui, QtCore
+import vlc
+from network import Client
+
+
+class MiniPlayer(QtWidgets.QMainWindow):
+    """Stripped-down PyQt5-based media player class to sync with "master" video.
+    """
+
+    def __init__(self, data_queue, master=None):
+        QtWidgets.QMainWindow.__init__(self, master)
+        self.setWindowTitle("Mini Player")
+        self.statusbar = self.statusBar()
+        self.statusbar.showMessage("Ready")
+
+        # 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.init_ui()
+        self.open_file()
+
+        self.timer = QtCore.QTimer(self)
+        self.timer.setInterval(10)
+        self.timer.timeout.connect(self.update_ui)
+
+        self.data_queue = data_queue
+        self.timer.start()
+
+    def init_ui(self):
+        """Set up the user interface
+        """
+        self.widget = QtWidgets.QWidget(self)
+        self.setCentralWidget(self.widget)
+
+        # In this widget, the video will be drawn
+        if platform.system() == "Darwin":  # for MacOS
+            self.videoframe = QtWidgets.QMacCocoaViewContainer(0)
+        else:
+            self.videoframe = QtWidgets.QFrame()
+
+        self.palette = self.videoframe.palette()
+        self.palette.setColor(QtGui.QPalette.Window, QtGui.QColor(0, 0, 0))
+        self.videoframe.setPalette(self.palette)
+        self.videoframe.setAutoFillBackground(True)
+
+        self.vboxlayout = QtWidgets.QVBoxLayout()
+        self.vboxlayout.addWidget(self.videoframe)
+        self.widget.setLayout(self.vboxlayout)
+
+    def open_file(self):
+        """Open a media file in a MediaPlayer
+        """
+        dialog_txt = "Choose Media File"
+        filename = QtWidgets.QFileDialog.getOpenFileName(self, dialog_txt, os.path.expanduser('~'))
+        if not filename[0]:
+            return
+
+        # getOpenFileName returns a tuple, so use only the actual file name
+        self.media = self.instance.media_new(filename[0])
+
+        # Put the media in the media player
+        self.mediaplayer.set_media(self.media)
+
+        # Parse the metadata of the file
+        self.media.parse()
+
+        # Set the title of the track as the window title
+        self.setWindowTitle("{}".format(self.media.get_meta(0)))
+
+        # The media player has to be 'connected' to the QFrame (otherwise the
+        # video would be displayed in it's own window). This is platform
+        # specific, so we must give the ID of the QFrame (or similar object) to
+        # vlc. Different platforms have different functions for this
+        if platform.system() == "Linux":  # for Linux using the X Server
+            self.mediaplayer.set_xwindow(int(self.videoframe.winId()))
+        elif platform.system() == "Windows":  # for Windows
+            self.mediaplayer.set_hwnd(int(self.videoframe.winId()))
+        elif platform.system() == "Darwin":  # for MacOS
+            self.mediaplayer.set_nsobject(int(self.videoframe.winId()))
+
+        # Start playing the video as soon as it loads
+        self.mediaplayer.play()
+
+    def update_ui(self):
+        self.update_statusbar()
+
+        try:
+            val = self.data_queue.get(block=False)
+        except queue.Empty:
+            return
+
+        if val == '<':
+            self.mediaplayer.set_rate(self.mediaplayer.get_rate() * 0.5)
+            return
+        if val == '>':
+            self.mediaplayer.set_rate(self.mediaplayer.get_rate() * 2)
+            return
+        if val == 'P':
+            self.mediaplayer.play()
+            return
+        if val == 'p':
+            self.mediaplayer.pause()
+            return
+        if val == 'S':
+            self.mediaplayer.stop()
+            return
+
+        val = int(val)
+        if val != self.mediaplayer.get_time():
+            self.mediaplayer.set_time(val)
+
+    def update_statusbar(self):
+        mtime = QtCore.QTime(0, 0, 0, 0)
+        time = mtime.addMSecs(self.mediaplayer.get_time())
+        self.statusbar.showMessage(time.toString())
+
+
+def main():
+    """Entry point for our simple vlc player
+    """
+    app = QtWidgets.QApplication(sys.argv)
+
+    data_queue = queue.Queue()
+
+    player = MiniPlayer(data_queue)
+    player.show()
+    player.resize(480, 480)
+
+    _ = Client("localhost", 10000, data_queue)
+    sys.exit(app.exec_())
+
+
+if __name__ == "__main__":
+    main()
diff --git a/generated/3.0/examples/video_sync/network.py b/generated/3.0/examples/video_sync/network.py
new file mode 100644 (file)
index 0000000..d657764
--- /dev/null
@@ -0,0 +1,175 @@
+#
+# PyQt5-based video-sync example for VLC Python bindings
+# Copyright (C) 2009-2010 the VideoLAN team
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+#
+"""
+The client/server classes that keep multiple VLC python bindings players
+synchronized.
+
+Author: Saveliy Yusufov, Columbia University, sy2685@columbia.edu
+Date: 25 January 2019
+"""
+
+import os
+import platform
+import socket
+import threading
+import sys
+import logging
+from concurrent import futures
+
+logger = logging.getLogger(__name__)
+logger.setLevel(logging.DEBUG)
+
+logger.setLevel(logging.DEBUG)
+
+# create console handler and set level to debug
+ch = logging.StreamHandler()
+ch.setLevel(logging.DEBUG)
+
+# create formatter
+formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
+
+# add formatter to ch
+ch.setFormatter(formatter)
+
+# add ch to logger
+logger.addHandler(ch)
+
+
+class Server:
+    """Data sender server"""
+
+    def __init__(self, host, port, data_queue):
+
+        if platform.system() == "Windows":
+
+            # Create a TCP/IP socket
+            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+
+            # Bind the socket to the port
+            logger.info("Server started on %s port %s", host, port)
+            self.sock.bind((host, port))
+
+        else:
+            server_address = "./uds_socket"
+
+            # Make sure the socket does not already exist
+            try:
+                os.unlink(server_address)
+            except OSError:
+                if os.path.exists(server_address):
+                    raise
+
+            # Create a UDS socket
+            self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+
+            # Bind the socket to the address
+            logger.info("Server starting up on %s", server_address)
+            self.sock.bind(server_address)
+
+        # Listen for incoming connections
+        self.sock.listen(5)
+
+        self.clients = set()
+        self.data_queue = data_queue
+        listener_thread = threading.Thread(target=self.listen_for_clients, args=())
+        listener_thread.daemon = True
+        listener_thread.start()
+
+    def listen_for_clients(self):
+        logger.info("Now listening for clients")
+        t = threading.Thread(target=self.data_sender, args=())
+        t.daemon = True
+        t.start()
+
+        while True:
+            client, _ = self.sock.accept()
+            logger.info("Accepted Connection from: %s", client)
+            self.clients.add(client)
+
+    def data_sender(self):
+        while True:
+            data = "{},".format(self.data_queue.get())
+
+            with futures.ThreadPoolExecutor(max_workers=5) as ex:
+                for client in self.clients:
+                    ex.submit(self.sendall, client, data.encode())
+
+    def sendall(self, client, data):
+        """Wraps socket module's `sendall` function"""
+        try:
+            client.sendall(data)
+        except socket.error:
+            logger.exception("Connection to client: %s was broken!", client)
+            client.close()
+            self.clients.remove(client)
+
+
+class Client:
+    """Data receiver client"""
+
+    def __init__(self, address, port, data_queue):
+        self.data_queue = data_queue
+
+        if platform.system() == "Windows":
+
+            # Create a TCP/IP socket
+            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+
+            # Connect the socket to the port where the server is listening
+            logger.info("Connecting to %s port %s", address, port)
+            self.sock.connect((address, port))
+        else:
+
+            # Create a UDS socket
+            self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+
+            # Connect the socket to the port where the server is listening
+            server_address = "./uds_socket"
+            logger.info("New client connecting to %s", server_address)
+
+            try:
+                self.sock.connect(server_address)
+            except socket.error:
+                logger.exception()
+                sys.exit(1)
+
+        thread = threading.Thread(target=self.data_receiver, args=())
+        thread.daemon = True
+        thread.start()
+
+    def data_receiver(self):
+        """Handles receiving, parsing, and queueing data"""
+        logger.info("New data receiver thread started.")
+
+        try:
+            while True:
+                data = self.sock.recv(4096)
+                if data:
+                    data = data.decode()
+
+                    for char in data.split(','):
+                        if char:
+                            if char == 'd':
+                                self.data_queue.queue.clear()
+                            else:
+                                self.data_queue.put(char)
+        except:
+            logger.exception("Closing socket: %s", self.sock)
+            self.sock.close()
+            return
index 2db9e39..3556ec8 100644 (file)
@@ -4,7 +4,7 @@ use_setuptools()
 from setuptools import setup
 
 setup(name='python-vlc',
-      version = '3.0.4107',
+      version = '3.0.6108',
       author='Olivier Aubert',
       author_email='contact@olivieraubert.net',
       maintainer='Olivier Aubert',
@@ -35,5 +35,5 @@ setup(name='python-vlc',
       player. Note that it relies on an already present install of VLC.
 
       It has been automatically generated from the include files of
-      vlc 3.0.4, using generator 1.7.
+      vlc 3.0.6, using generator 1.8.
       """)
index c8f1ef2..63dd198 100644 (file)
@@ -52,10 +52,10 @@ from inspect import getargspec
 import logging
 logger = logging.getLogger(__name__)
 
-__version__ = "3.0.4107"
-__libvlc_version__ = "3.0.4"
-__generator_version__ = "1.7"
-build_date  = "Mon Nov 12 23:07:30 2018 3.0.4"
+__version__ = "3.0.6108"
+__libvlc_version__ = "3.0.6"
+__generator_version__ = "1.8"
+build_date  = "Sun Feb 24 16:20:25 2019 3.0.6"
 
 # The libvlc doc states that filenames are expected to be in UTF8, do
 # not rely on sys.getfilesystemencoding() which will be confused,
@@ -124,13 +124,7 @@ def find_lib():
     if dll is not None:
         return dll, plugin_path
 
-    if sys.platform.startswith('linux'):
-        p = find_library('vlc')
-        try:
-            dll = ctypes.CDLL(p)
-        except OSError:  # may fail
-            dll = ctypes.CDLL('libvlc.so.5')
-    elif sys.platform.startswith('win'):
+    if sys.platform.startswith('win'):
         libname = 'libvlc.dll'
         p = find_library(libname)
         if p is None:
@@ -195,7 +189,17 @@ def find_lib():
             dll = ctypes.CDLL('libvlc.dylib')
 
     else:
-        raise NotImplementedError('%s: %s not supported' % (sys.argv[0], sys.platform))
+        # All other OSes (linux, freebsd...)
+        p = find_library('vlc')
+        try:
+            dll = ctypes.CDLL(p)
+        except OSError:  # may fail
+            dll = None
+        if dll is None:
+            try:
+                dll = ctypes.CDLL('libvlc.so.5')
+            except:
+                raise NotImplementedError('Cannot find libvlc lib')
 
     return (dll, plugin_path)
 
old mode 100755 (executable)
new mode 100644 (file)
index fd1369f..4c1b33b
@@ -52,10 +52,10 @@ from inspect import getargspec
 import logging
 logger = logging.getLogger(__name__)
 
-__version__ = "4.0.0-dev-5376-g71cb852338107"
-__libvlc_version__ = "4.0.0-dev-5376-g71cb852338"
-__generator_version__ = "1.7"
-build_date  = "Mon Nov 12 23:07:30 2018 4.0.0-dev-5376-g71cb852338"
+__version__ = "4.0.0-dev-6311-gd5ccdffa65108"
+__libvlc_version__ = "4.0.0-dev-6311-gd5ccdffa65"
+__generator_version__ = "1.8"
+build_date  = "Sun Feb 24 16:20:24 2019 4.0.0-dev-6311-gd5ccdffa65"
 
 # The libvlc doc states that filenames are expected to be in UTF8, do
 # not rely on sys.getfilesystemencoding() which will be confused,
@@ -124,13 +124,7 @@ def find_lib():
     if dll is not None:
         return dll, plugin_path
 
-    if sys.platform.startswith('linux'):
-        p = find_library('vlc')
-        try:
-            dll = ctypes.CDLL(p)
-        except OSError:  # may fail
-            dll = ctypes.CDLL('libvlc.so.5')
-    elif sys.platform.startswith('win'):
+    if sys.platform.startswith('win'):
         libname = 'libvlc.dll'
         p = find_library(libname)
         if p is None:
@@ -195,7 +189,17 @@ def find_lib():
             dll = ctypes.CDLL('libvlc.dylib')
 
     else:
-        raise NotImplementedError('%s: %s not supported' % (sys.argv[0], sys.platform))
+        # All other OSes (linux, freebsd...)
+        p = find_library('vlc')
+        try:
+            dll = ctypes.CDLL(p)
+        except OSError:  # may fail
+            dll = None
+        if dll is None:
+            try:
+                dll = ctypes.CDLL('libvlc.so.5')
+            except:
+                raise NotImplementedError('Cannot find libvlc lib')
 
     return (dll, plugin_path)
 
@@ -893,16 +897,16 @@ TeletextKey.index  = TeletextKey(6881280)
 TeletextKey.red    = TeletextKey(7471104)
 TeletextKey.yellow = TeletextKey(7929856)
 
-class GlEngine(_Enum):
-    '''Enumeration of the opengl engine to be used
-can be passed to @a libvlc_video_set_opengl_callbacks.
+class VideoEngine(_Enum):
+    '''Enumeration of the video engine to be used on output.
+can be passed to @a libvlc_video_set_output_callbacks.
     '''
     _enum_names_ = {
         0: 'opengl',
         1: 'gles2',
     }
-GlEngine.gles2  = GlEngine(1)
-GlEngine.opengl = GlEngine(0)
+VideoEngine.gles2  = VideoEngine(1)
+VideoEngine.opengl = VideoEngine(0)
 
 class VideoLogoOption(_Enum):
     '''Option values for libvlc_video_{get,set}_logo_{int,string}.
@@ -1120,44 +1124,44 @@ class VideoCleanupCb(ctypes.c_void_p):
     @param opaque: private pointer as passed to L{libvlc_video_set_callbacks}() (and possibly modified by @ref libvlc_video_format_cb) [IN].
     """
     pass
-class GlSetupCb(ctypes.c_void_p):
+class VideoSetupCb(ctypes.c_void_p):
     """Callback prototype called to initialize user data.
-    @param opaque: private pointer passed to the @a L{libvlc_video_set_opengl_callbacks}() [IN].
+    @param opaque: private pointer passed to the @a L{libvlc_video_set_output_callbacks}() [IN].
     @return: true on success.
     @version: LibVLC 4.0.0 or later.
     """
     pass
-class GlCleanupCb(ctypes.c_void_p):
+class VideoCleanupCb(ctypes.c_void_p):
     """Callback prototype called to release user data.
-    @param opaque: private pointer passed to the @a L{libvlc_video_set_opengl_callbacks}() [IN].
+    @param opaque: private pointer passed to the @a L{libvlc_video_set_output_callbacks}() [IN].
     @version: LibVLC 4.0.0 or later.
     """
     pass
-class GlResizeCb(ctypes.c_void_p):
+class VideoUpdateOutputCb(ctypes.c_void_p):
     """Callback prototype called on video size changes.
-    @param opaque: private pointer passed to the @a L{libvlc_video_set_opengl_callbacks}() [IN].
+    @param opaque: private pointer passed to the @a L{libvlc_video_set_output_callbacks}() [IN].
     @param width: video width in pixel [IN].
     @param height: video height in pixel [IN].
     @version: LibVLC 4.0.0 or later.
     """
     pass
-class GlSwapCb(ctypes.c_void_p):
+class VideoSwapCb(ctypes.c_void_p):
     """Callback prototype called after performing drawing calls.
-    @param opaque: private pointer passed to the @a L{libvlc_video_set_opengl_callbacks}() [IN].
+    @param opaque: private pointer passed to the @a L{libvlc_video_set_output_callbacks}() [IN].
     @version: LibVLC 4.0.0 or later.
     """
     pass
-class GlMakecurrentCb(ctypes.c_void_p):
+class VideoMakecurrentCb(ctypes.c_void_p):
     """Callback prototype to set up the OpenGL context for rendering.
-    @param opaque: private pointer passed to the @a L{libvlc_video_set_opengl_callbacks}() [IN].
+    @param opaque: private pointer passed to the @a L{libvlc_video_set_output_callbacks}() [IN].
     @param enter: true to set the context as current, false to unset it [IN].
     @return: true on success.
     @version: LibVLC 4.0.0 or later.
     """
     pass
-class GlGetprocaddressCb(ctypes.c_void_p):
+class VideoGetprocaddressCb(ctypes.c_void_p):
     """Callback prototype to load opengl functions.
-    @param opaque: private pointer passed to the @a L{libvlc_video_set_opengl_callbacks}() [IN].
+    @param opaque: private pointer passed to the @a L{libvlc_video_set_output_callbacks}() [IN].
     @param fct_name: name of the opengl function to load.
     @return: a pointer to the named OpenGL function the None otherwise.
     @version: LibVLC 4.0.0 or later.
@@ -1323,39 +1327,39 @@ class CallbackDecorators(object):
     VideoCleanupCb.__doc__ = '''Callback prototype to configure picture buffers format.
         @param opaque: private pointer as passed to L{libvlc_video_set_callbacks}() (and possibly modified by @ref libvlc_video_format_cb) [IN].
     '''
-    GlSetupCb = ctypes.CFUNCTYPE(ctypes.POINTER(ctypes.c_bool), ctypes.c_void_p)
-    GlSetupCb.__doc__ = '''Callback prototype called to initialize user data.
-        @param opaque: private pointer passed to the @a L{libvlc_video_set_opengl_callbacks}() [IN].
+    VideoSetupCb = ctypes.CFUNCTYPE(ctypes.POINTER(ctypes.c_bool), ctypes.c_void_p)
+    VideoSetupCb.__doc__ = '''Callback prototype called to initialize user data.
+        @param opaque: private pointer passed to the @a L{libvlc_video_set_output_callbacks}() [IN].
         @return: true on success.
         @version: LibVLC 4.0.0 or later.
     '''
-    GlCleanupCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p)
-    GlCleanupCb.__doc__ = '''Callback prototype called to release user data.
-        @param opaque: private pointer passed to the @a L{libvlc_video_set_opengl_callbacks}() [IN].
+    VideoCleanupCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p)
+    VideoCleanupCb.__doc__ = '''Callback prototype called to release user data.
+        @param opaque: private pointer passed to the @a L{libvlc_video_set_output_callbacks}() [IN].
         @version: LibVLC 4.0.0 or later.
     '''
-    GlResizeCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint, ctypes.c_uint)
-    GlResizeCb.__doc__ = '''Callback prototype called on video size changes.
-        @param opaque: private pointer passed to the @a L{libvlc_video_set_opengl_callbacks}() [IN].
+    VideoUpdateOutputCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint, ctypes.c_uint)
+    VideoUpdateOutputCb.__doc__ = '''Callback prototype called on video size changes.
+        @param opaque: private pointer passed to the @a L{libvlc_video_set_output_callbacks}() [IN].
         @param width: video width in pixel [IN].
         @param height: video height in pixel [IN].
         @version: LibVLC 4.0.0 or later.
     '''
-    GlSwapCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p)
-    GlSwapCb.__doc__ = '''Callback prototype called after performing drawing calls.
-        @param opaque: private pointer passed to the @a L{libvlc_video_set_opengl_callbacks}() [IN].
+    VideoSwapCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p)
+    VideoSwapCb.__doc__ = '''Callback prototype called after performing drawing calls.
+        @param opaque: private pointer passed to the @a L{libvlc_video_set_output_callbacks}() [IN].
         @version: LibVLC 4.0.0 or later.
     '''
-    GlMakecurrentCb = ctypes.CFUNCTYPE(ctypes.POINTER(ctypes.c_bool), ctypes.c_void_p, ctypes.c_bool)
-    GlMakecurrentCb.__doc__ = '''Callback prototype to set up the OpenGL context for rendering.
-        @param opaque: private pointer passed to the @a L{libvlc_video_set_opengl_callbacks}() [IN].
+    VideoMakecurrentCb = ctypes.CFUNCTYPE(ctypes.POINTER(ctypes.c_bool), ctypes.c_void_p, ctypes.c_bool)
+    VideoMakecurrentCb.__doc__ = '''Callback prototype to set up the OpenGL context for rendering.
+        @param opaque: private pointer passed to the @a L{libvlc_video_set_output_callbacks}() [IN].
         @param enter: true to set the context as current, false to unset it [IN].
         @return: true on success.
         @version: LibVLC 4.0.0 or later.
     '''
-    GlGetprocaddressCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_char_p)
-    GlGetprocaddressCb.__doc__ = '''Callback prototype to load opengl functions.
-        @param opaque: private pointer passed to the @a L{libvlc_video_set_opengl_callbacks}() [IN].
+    VideoGetprocaddressCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_char_p)
+    VideoGetprocaddressCb.__doc__ = '''Callback prototype to load opengl functions.
+        @param opaque: private pointer passed to the @a L{libvlc_video_set_output_callbacks}() [IN].
         @param fct_name: name of the opengl function to load.
         @return: a pointer to the named OpenGL function the None otherwise.
         @version: LibVLC 4.0.0 or later.
@@ -3283,24 +3287,6 @@ class MediaPlayer(_Ctype):
         return libvlc_video_set_callbacks(self, lock, unlock, display, opaque)
 
 
-    def video_set_opengl_callbacks(self, gl_engine, setup_cb, cleanup_cb, resize_cb, swap_cb, makeCurrent_cb, getProcAddress_cb, opaque):
-        '''Set callbacks and data to render decoded video to a custom OpenGL texture
-        @warning: VLC will perform video rendering in its own thread and at its own rate,
-        You need to provide your own synchronisation mechanism.
-        OpenGL context need to be created before playing a media.
-        @param gl_engine: the OpenGL engine to use.
-        @param setup_cb: callback called to initialize user data.
-        @param cleanup_cb: callback called to clean up user data.
-        @param resize_cb: callback called to get the size of the video.
-        @param swap_cb: callback called after rendering a video frame (cannot be None).
-        @param makeCurrent_cb: callback called to enter/leave the opengl context (cannot be None).
-        @param getProcAddress_cb: opengl function loading callback (cannot be None).
-        @param opaque: private pointer passed to callbacks.
-        @version: LibVLC 4.0.0 or later.
-        '''
-        return libvlc_video_set_opengl_callbacks(self, gl_engine, setup_cb, cleanup_cb, resize_cb, swap_cb, makeCurrent_cb, getProcAddress_cb, opaque)
-
-
     def video_set_format(self, chroma, width, height, pitch):
         '''Set decoded video chroma and dimensions.
         This only works in combination with L{video_set_callbacks}(),
@@ -3325,6 +3311,24 @@ class MediaPlayer(_Ctype):
         return libvlc_video_set_format_callbacks(self, setup, cleanup)
 
 
+    def video_set_output_callbacks(self, engine, setup_cb, cleanup_cb, update_output_cb, swap_cb, makeCurrent_cb, getProcAddress_cb, opaque):
+        '''Set callbacks and data to render decoded video to a custom texture
+        @warning: VLC will perform video rendering in its own thread and at its own rate,
+        You need to provide your own synchronisation mechanism.
+        OpenGL context need to be created before playing a media.
+        @param engine: the GPU engine to use.
+        @param setup_cb: callback called to initialize user data.
+        @param cleanup_cb: callback called to clean up user data.
+        @param update_output_cb: callback called to get the size of the video.
+        @param swap_cb: callback called after rendering a video frame (cannot be None).
+        @param makeCurrent_cb: callback called to enter/leave the opengl context (cannot be None for \ref libvlc_video_engine_opengl and for \ref libvlc_video_engine_gles2).
+        @param getProcAddress_cb: opengl function loading callback (cannot be None for \ref libvlc_video_engine_opengl and for \ref libvlc_video_engine_gles2).
+        @param opaque: private pointer passed to callbacks \libvlc_return_bool.
+        @version: LibVLC 4.0.0 or later.
+        '''
+        return libvlc_video_set_output_callbacks(self, engine, setup_cb, cleanup_cb, update_output_cb, swap_cb, makeCurrent_cb, getProcAddress_cb, opaque)
+
+
     def set_nsobject(self, drawable):
         '''Set the NSView handler where the media player should render its video output.
         Use the vout called "macosx".
@@ -3452,7 +3456,7 @@ class MediaPlayer(_Ctype):
         '''Sets a fixed decoded audio format.
         This only works in combination with L{audio_set_callbacks}(),
         and is mutually exclusive with L{audio_set_format_callbacks}().
-        @param format: a four-characters string identifying the sample format (e.g. "S16N" or "FL32").
+        @param format: a four-characters string identifying the sample format (e.g. "S16N" or "f32l").
         @param rate: sample rate (expressed in Hz).
         @param channels: channels count.
         @version: LibVLC 2.0.0 or later.
@@ -6009,27 +6013,6 @@ def libvlc_video_set_callbacks(mp, lock, unlock, display, opaque):
                     None, MediaPlayer, VideoLockCb, VideoUnlockCb, VideoDisplayCb, ctypes.c_void_p)
     return f(mp, lock, unlock, display, opaque)
 
-def libvlc_video_set_opengl_callbacks(mp, gl_engine, setup_cb, cleanup_cb, resize_cb, swap_cb, makeCurrent_cb, getProcAddress_cb, opaque):
-    '''Set callbacks and data to render decoded video to a custom OpenGL texture
-    @warning: VLC will perform video rendering in its own thread and at its own rate,
-    You need to provide your own synchronisation mechanism.
-    OpenGL context need to be created before playing a media.
-    @param mp: the media player.
-    @param gl_engine: the OpenGL engine to use.
-    @param setup_cb: callback called to initialize user data.
-    @param cleanup_cb: callback called to clean up user data.
-    @param resize_cb: callback called to get the size of the video.
-    @param swap_cb: callback called after rendering a video frame (cannot be None).
-    @param makeCurrent_cb: callback called to enter/leave the opengl context (cannot be None).
-    @param getProcAddress_cb: opengl function loading callback (cannot be None).
-    @param opaque: private pointer passed to callbacks.
-    @version: LibVLC 4.0.0 or later.
-    '''
-    f = _Cfunctions.get('libvlc_video_set_opengl_callbacks', None) or \
-        _Cfunction('libvlc_video_set_opengl_callbacks', ((1,), (1,), (1,), (1,), (1,), (1,), (1,), (1,), (1,),), None,
-                    None, MediaPlayer, GlEngine, GlSetupCb, GlCleanupCb, GlResizeCb, GlSwapCb, GlMakecurrentCb, GlGetprocaddressCb, ctypes.c_void_p)
-    return f(mp, gl_engine, setup_cb, cleanup_cb, resize_cb, swap_cb, makeCurrent_cb, getProcAddress_cb, opaque)
-
 def libvlc_video_set_format(mp, chroma, width, height, pitch):
     '''Set decoded video chroma and dimensions.
     This only works in combination with L{libvlc_video_set_callbacks}(),
@@ -6060,6 +6043,27 @@ def libvlc_video_set_format_callbacks(mp, setup, cleanup):
                     None, MediaPlayer, VideoFormatCb, VideoCleanupCb)
     return f(mp, setup, cleanup)
 
+def libvlc_video_set_output_callbacks(mp, engine, setup_cb, cleanup_cb, update_output_cb, swap_cb, makeCurrent_cb, getProcAddress_cb, opaque):
+    '''Set callbacks and data to render decoded video to a custom texture
+    @warning: VLC will perform video rendering in its own thread and at its own rate,
+    You need to provide your own synchronisation mechanism.
+    OpenGL context need to be created before playing a media.
+    @param mp: the media player.
+    @param engine: the GPU engine to use.
+    @param setup_cb: callback called to initialize user data.
+    @param cleanup_cb: callback called to clean up user data.
+    @param update_output_cb: callback called to get the size of the video.
+    @param swap_cb: callback called after rendering a video frame (cannot be None).
+    @param makeCurrent_cb: callback called to enter/leave the opengl context (cannot be None for \ref libvlc_video_engine_opengl and for \ref libvlc_video_engine_gles2).
+    @param getProcAddress_cb: opengl function loading callback (cannot be None for \ref libvlc_video_engine_opengl and for \ref libvlc_video_engine_gles2).
+    @param opaque: private pointer passed to callbacks \libvlc_return_bool.
+    @version: LibVLC 4.0.0 or later.
+    '''
+    f = _Cfunctions.get('libvlc_video_set_output_callbacks', None) or \
+        _Cfunction('libvlc_video_set_output_callbacks', ((1,), (1,), (1,), (1,), (1,), (1,), (1,), (1,), (1,),), None,
+                    ctypes.c_int, MediaPlayer, VideoEngine, VideoSetupCb, VideoCleanupCb, VideoUpdateOutputCb, VideoSwapCb, VideoMakecurrentCb, VideoGetprocaddressCb, ctypes.c_void_p)
+    return f(mp, engine, setup_cb, cleanup_cb, update_output_cb, swap_cb, makeCurrent_cb, getProcAddress_cb, opaque)
+
 def libvlc_media_player_set_nsobject(p_mi, drawable):
     '''Set the NSView handler where the media player should render its video output.
     Use the vout called "macosx".
@@ -6227,7 +6231,7 @@ def libvlc_audio_set_format(mp, format, rate, channels):
     This only works in combination with L{libvlc_audio_set_callbacks}(),
     and is mutually exclusive with L{libvlc_audio_set_format_callbacks}().
     @param mp: the media player.
-    @param format: a four-characters string identifying the sample format (e.g. "S16N" or "FL32").
+    @param format: a four-characters string identifying the sample format (e.g. "S16N" or "f32l").
     @param rate: sample rate (expressed in Hz).
     @param channels: channels count.
     @version: LibVLC 2.0.0 or later.