diff --git a/src/modules/sdl2/consumer_sdl2.c b/src/modules/sdl2/consumer_sdl2.c index 9ac1b1f1b..c3ff8e838 100644 --- a/src/modules/sdl2/consumer_sdl2.c +++ b/src/modules/sdl2/consumer_sdl2.c @@ -573,6 +573,7 @@ static int consumer_play_video(consumer_sdl self, mlt_frame frame) int video_off = mlt_properties_get_int(properties, "video_off"); int preview_off = mlt_properties_get_int(properties, "preview_off"); int display_off = video_off | preview_off; + uintptr_t window_id = mlt_properties_get_int(self->properties, "window_id"); if (self->running && !display_off) { if (!self->sdl_window) { @@ -585,13 +586,24 @@ static int consumer_play_video(consumer_sdl self, mlt_frame frame) mlt_frame_get_image(frame, &image, &vfmt, &width, &height, 0); if (self->running) { - // Determine window's new display aspect ratio - int x = mlt_properties_get_int(properties, "window_width"); - if (x && x != self->window_width) - self->window_width = x; - x = mlt_properties_get_int(properties, "window_height"); - if (x && x != self->window_height) - self->window_height = x; + // Determine window's new display aspect ratio, and resize if it's an existing window + int w = mlt_properties_get_int(properties, "window_width"); + int h = mlt_properties_get_int(properties, "window_height"); + int width_changed = (w && w != self->window_width); + int height_changed = (h && h != self->window_height); + + if (width_changed || height_changed) { + if (width_changed) { + self->window_width = w; + } + if (height_changed) { + self->window_height = h; + } + if (window_id) { + SDL_SetWindowSize(self->sdl_window, self->window_width, self->window_height); + } + } + double this_aspect = (double) self->window_width / self->window_height; // Get the display aspect ratio diff --git a/src/swig/python/play_gtk.py b/src/swig/python/play_gtk.py new file mode 100644 index 000000000..572e896ca --- /dev/null +++ b/src/swig/python/play_gtk.py @@ -0,0 +1,142 @@ +import gi +import sys +import mlt7 as mlt + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk, Gdk, GObject, GLib + +VIDEO = 'video.mp4' + +class VideoPlayer(Gtk.Window): + def __init__(self): + super().__init__(title="MLT Video Player") + self.set_default_size(800, 600) + self.connect("destroy", self.on_destroy) + + # Initialize the mlt system + mlt.Factory().init() + + # Create a default profile + self.profile = mlt.Profile() + + # Create a producer for the video + self.producer = mlt.Producer(self.profile, VIDEO) + if self.producer.is_valid(): + self.profile.from_producer(self.producer) + self.producer = mlt.Producer(self.profile, VIDEO) + + # Create the consumer for rendering using SDL2 + self.consumer = mlt.Consumer(self.profile, "sdl2") + + # GTK Layout + self.layout = Gtk.VBox(spacing=6) + self.add(self.layout) + + # Create the drawing area for the video + self.drawing_area = Gtk.DrawingArea() + self.drawing_area.set_size_request(800, 450) + self.drawing_area.connect("size-allocate",self.drawing_area_resized) + self.layout.pack_start(self.drawing_area, True, True, 0) + self.drawing_area.realize() + print(int(self.drawing_area.get_window().get_xid())) + self.consumer.set('window_id', str(self.drawing_area.get_window().get_xid())) + + if not self.consumer.is_valid(): + print("Failed to open the sdl2 consumer") + sys.exit(1) + + # Create a multitrack playlist and set it to the tractor + self.playlist = mlt.Playlist(self.profile) + self.playlist.append(self.producer) + + self.tractor = mlt.Tractor() + self.tractor.set_track(self.playlist, 0) + + # Connect the producer to the consumer + self.consumer.connect(self.tractor) + + # Control Buttons + controls_box = Gtk.HBox(spacing=6) + self.layout.pack_start(controls_box, False, False, 0) + + # Play button + self.play_button = Gtk.Button.new_with_label("Play") + self.play_button.connect("clicked", self.play_video) + controls_box.pack_start(self.play_button, False, False, 0) + + # Pause button + self.pause_button = Gtk.Button.new_with_label("Pause") + self.pause_button.connect("clicked", self.pause_video) + controls_box.pack_start(self.pause_button, False, False, 0) + + # Stop button + self.stop_button = Gtk.Button.new_with_label("Stop") + self.stop_button.connect("clicked", self.stop_video) + controls_box.pack_start(self.stop_button, False, False, 0) + + # Slider + self.timeline_slider = Gtk.Scale.new_with_range(Gtk.Orientation.HORIZONTAL, 0, 1000, 1) + self.timeline_slider.set_draw_value(False) + self.slider_handler_id = self.timeline_slider.connect("value-changed", self.on_slider_changed) + self.timeline_slider.connect("button-release-event", self.on_slider_button_release) + controls_box.pack_start(self.timeline_slider, True, True, 0) + + # Timer to update the slider + self.update_timer = GLib.timeout_add(1000 // 24, self.update_slider) + + # Start the consumer + self.consumer.start() + self.pause_video() + + def drawing_area_resized(self, widget, _): + self.consumer.set('window_width', widget.get_allocated_width()) + self.consumer.set('window_height', widget.get_allocated_height()) + + def update_slider(self): + position = self.consumer.position() + playtime = self.tractor.get_playtime() + if playtime > 0: + value = int((position / playtime) * 1000) + self.timeline_slider.handler_block(self.slider_handler_id) + self.timeline_slider.set_value(value) + self.timeline_slider.handler_unblock(self.slider_handler_id) + return True + + def play_video(self, button): + self.tractor.set_speed(1) + self.consumer.set('volume', 1) + + def pause_video(self, button=None): + if not self.consumer.is_stopped(): + self.tractor.set_speed(0) + self.consumer.set('volume', 0) + + def stop_video(self, button): + self.tractor.seek(0) + self.tractor.set_speed(0) + self.timeline_slider.set_value(0) + + def on_slider_changed(self, slider): + value = slider.get_value() + self.consumer.set('volume', 0) + playtime = self.tractor.get_playtime() + self.tractor.seek(int((value / 1000.0) * playtime)) + + def on_slider_button_release(self, slider, _): + if not self.tractor.get_speed() == 0: + self.consumer.set('volume', 1) + + def on_destroy(self, widget): + self.consumer.stop() + mlt.Factory().close() + Gtk.main_quit() + +if __name__ == "__main__": + # Initialize GTK + GObject.threads_init() + Gtk.init(sys.argv) + + # Create and run the video player + player = VideoPlayer() + player.show_all() + Gtk.main() diff --git a/src/swig/python/play_pyqt6.py b/src/swig/python/play_pyqt6.py new file mode 100644 index 000000000..4775b4d12 --- /dev/null +++ b/src/swig/python/play_pyqt6.py @@ -0,0 +1,143 @@ +import mlt7 as mlt +import sys + +from PyQt6.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QSlider, QStyle +from PyQt6.QtCore import Qt, QTimer + +VIDEO = 'video.mp4' + + +class VideoPlayer(QWidget): + def __init__(self): + super().__init__() + self.setWindowTitle("MLT Video Player") + + layout = QVBoxLayout() + + class video_viewer(QWidget): + """The main window (QWidget) class""" + def __init__(self): + super().__init__() + self.setMinimumSize(380, 260) + + def resizeEvent(self, event): + self.window().consumer.set('window_width', self.width()) + self.window().consumer.set('window_height', self.height()) + event.accept() + + self.video_viewer = video_viewer() + layout.addWidget(self.video_viewer, 1) + + buttons_layout = QHBoxLayout() + + self.play_button = QPushButton() + self.play_button.setFlat(True) + self.play_button.setIcon(self.style().standardIcon(QStyle.StandardPixmap.SP_MediaPlay)) + self.play_button.clicked.connect(self.play_video) + buttons_layout.addWidget(self.play_button) + + self.pause_button = QPushButton() + self.pause_button.setFlat(True) + self.pause_button.setIcon(self.style().standardIcon(QStyle.StandardPixmap.SP_MediaPause)) + self.pause_button.clicked.connect(self.pause_video) + buttons_layout.addWidget(self.pause_button) + + self.stop_button = QPushButton() + self.stop_button.setFlat(True) + self.stop_button.setIcon(self.style().standardIcon(QStyle.StandardPixmap.SP_MediaStop)) + self.stop_button.clicked.connect(self.stop_video) + buttons_layout.addWidget(self.stop_button) + + self.timeline_slider = QSlider(Qt.Orientation.Horizontal) + self.timeline_slider.setRange(0, 1000) + self.timeline_slider.sliderPressed.connect(self.stop_update_slider) + self.timeline_slider.sliderMoved.connect(self.timeline_slider_changed) + self.timeline_slider.sliderReleased.connect(self.set_volume_back_to_normal) + buttons_layout.addWidget(self.timeline_slider) + + layout.addLayout(buttons_layout) + + self.setLayout(layout) + + self.timer = QTimer(self) + self.timer.timeout.connect(self.update_slider) + + # Start the mlt system + mlt.Factory().init() + + # Establish a default (usually "dv_pal") profile + self.profile = mlt.Profile() + + # Create the producer + self.producer = mlt.Producer(self.profile, VIDEO) + + if self.producer.is_valid(): + # Derive a profile based on the producer + self.profile.from_producer(self.producer) + # Reload the producer using the derived profile + self.producer = mlt.Producer(self.profile, VIDEO) + + # Create the consumer + self.consumer = mlt.Consumer(self.profile, "sdl2") + self.consumer.set('window_id', str(int(self.video_viewer.winId()))) # PyQt6 requires str or int for set('window_id') + + if not self.consumer.is_valid(): + print("Failed to open the sdl2 consumer") + sys.exit(1) + + # Setup a multitrack + self.playlist = mlt.Playlist(self.profile) + self.playlist.append(self.producer) + + self.tractor = mlt.Tractor() + self.tractor.set_track(self.playlist, 0) + + # Connect the producer to the consumer + self.consumer.connect(self.tractor) + + # Start the consumer + self.consumer.start() + self.pause_video() + + def update_slider(self): + self.timeline_slider.setValue(int((self.consumer.position() / self.tractor.get_playtime()) * 1000)) + + def play_video(self): + self.tractor.set_speed(1) + self.consumer.set('volume', 1) + self.timer.start(int(1000 / 24)) + + def pause_video(self): + if not self.consumer.is_stopped(): + self.tractor.set_speed(0) + self.consumer.set('volume', 0) + + def stop_video(self): + self.tractor.seek(0) + self.tractor.set_speed(0) + self.timeline_slider.setValue(0) + self.timer.stop() + + def timeline_slider_changed(self, value): + self.consumer.set('volume', 0) + self.tractor.seek(int((value / 1000.0) * self.tractor.get_playtime())) + + def stop_update_slider(self): + self.timer.stop() + + def set_volume_back_to_normal(self): + if not self.tractor.get_speed() == 0: + self.consumer.set('volume', 1) + self.timer.start(int(1000 / 24)) + + def closeEvent(self, event): + self.consumer.stop() + event.accept() + +if __name__ == "__main__": + app = QApplication(sys.argv) + + player = VideoPlayer() + player.show() + + sys.exit(app.exec()) diff --git a/src/swig/python/play_pyside6.py b/src/swig/python/play_pyside6.py new file mode 100644 index 000000000..611626c5f --- /dev/null +++ b/src/swig/python/play_pyside6.py @@ -0,0 +1,144 @@ +import mlt7 as mlt +import sys + +from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QSlider, QStyle +from PySide6.QtCore import Qt, QTimer + +VIDEO = 'video.mp4' + + +class VideoPlayer(QWidget): + def __init__(self): + super().__init__() + self.setWindowTitle("MLT Video Player") + + layout = QVBoxLayout() + class video_viewer(QWidget): + """The main window (QWidget) class""" + def __init__(self): + super().__init__() + self.setMinimumSize(380, 260) + + def resizeEvent(self, event): + self.window().consumer.set('window_width', self.width()) + self.window().consumer.set('window_height', self.height()) + event.accept() + + self.video_viewer = video_viewer() + layout.addWidget(self.video_viewer, 1) + + buttons_layout = QHBoxLayout() + + self.play_button = QPushButton() + self.play_button.setFlat(True) + self.play_button.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay)) + self.play_button.clicked.connect(self.play_video) + buttons_layout.addWidget(self.play_button) + + self.pause_button = QPushButton() + self.pause_button.setFlat(True) + self.pause_button.setIcon(self.style().standardIcon(QStyle.SP_MediaPause)) + self.pause_button.clicked.connect(self.pause_video) + buttons_layout.addWidget(self.pause_button) + + self.stop_button = QPushButton() + self.stop_button.setFlat(True) + self.stop_button.setIcon(self.style().standardIcon(QStyle.SP_MediaStop)) + self.stop_button.clicked.connect(self.stop_video) + buttons_layout.addWidget(self.stop_button) + + self.timeline_slider = QSlider(Qt.Horizontal) + self.timeline_slider.setRange(0, 1000) + self.timeline_slider.sliderPressed.connect(self.stop_update_slider) + self.timeline_slider.sliderMoved.connect(self.timeline_slider_changed) + self.timeline_slider.sliderReleased.connect(self.set_volume_back_to_normal) + buttons_layout.addWidget(self.timeline_slider) + + layout.addLayout(buttons_layout) + + self.setLayout(layout) + + self.timer = QTimer(self) + self.timer.timeout.connect(self.update_slider) + + # Start the mlt system + mlt.Factory().init() + + # Establish a default (usually "dv_pal") profile + self.profile = mlt.Profile() + + # Create the producer + self.producer = mlt.Producer(self.profile, VIDEO) + + if self.producer.is_valid(): + # Derive a profile based on the producer + self.profile.from_producer(self.producer) + # Reload the producer using the derived profile + self.producer = mlt.Producer(self.profile, VIDEO) + + # Create the consumer + self.consumer = mlt.Consumer(self.profile, "sdl2") + self.consumer.set('window_id', self.video_viewer.winId()) + + if not self.consumer.is_valid(): + print("Failed to open the sdl2 consumer") + sys.exit(1) + + # Setup a multitrack + self.playlist = mlt.Playlist(self.profile) + self.playlist.append(self.producer) + + self.tractor = mlt.Tractor() + self.tractor.set_track(self.playlist, 0) + + # Connect the producer to the consumer + self.consumer.connect(self.tractor) + + # Start the consumer + self.consumer.start() + self.pause_video() + + def update_slider(self): + self.timeline_slider.setValue(int((self.consumer.position() / self.tractor.get_playtime()) * 1000)) + + def play_video(self): + self.tractor.set_speed(1) + self.consumer.set('volume', 1) + self.timer.start(1000/24) + + def pause_video(self): + if not self.consumer.is_stopped(): + self.tractor.set_speed(0) + self.consumer.set('volume', 0) + + def stop_video(self): + self.tractor.seek(0) + self.tractor.set_speed(0) + self.timeline_slider.setValue(0) + self.timer.stop() + + def timeline_slider_changed(self, value): + self.consumer.set('volume', 0) + self.tractor.seek(int((value / 1000.0) * self.tractor.get_playtime())) + + def stop_update_slider(self): + self.timer.stop() + + def set_volume_back_to_normal(self): + if not self.tractor.get_speed() == 0: + self.consumer.set('volume', 1) + self.timer.start(1000/24) + + def closeEvent(self, event): + self.consumer.stop() + event.accept() + +if __name__ == "__main__": + import sys + + app = QApplication(sys.argv) + + player = VideoPlayer() + player.show() + + sys.exit(app.exec()) \ No newline at end of file