简体   繁体   中英

Making GStreamer video/audio in Python smooth and loop

I'm trying to use GStreamer to loop MPEG-4 files smoothly. I want to play the video and audio if there is any and loop the playback. My app uses GTK for UI.

Right now I have three problems:

  1. The video playback is choppy/jerky on the Raspberry Pi 4, I'm running it on. By choppy/jerky, I mean that every ~1-2 seconds, playback freezes for some fraction of a second. When playing the same video in the VLC app, it is smooth.
  2. Audio is not played. Again, when played in VLC, the audio is there as expected. It was my understanding that playbin elements automatically play both audio and video.
  3. When the end of the video is reached, the last frame is frozen for 1-2 seconds before the video starts playing from the first frame again.

I currently have the following code.


video_player.py:

#!/usr/bin/python3
import os
import gi

gi.require_version("Gst", "1.0")
gi.require_version("Gtk", "3.0")
gi.require_version("GstVideo", "1.0")
from gi.repository import Gst, Gtk, GstVideo


class VideoPlayer(Gtk.DrawingArea):
    def __init__(self, video_uri: str, loop: bool):
        super().__init__()
        self.__loop = loop
        self.__video_uri = "file:///" + os.path.abspath(video_uri)
        self.__xid = None

        Gst.init(None)
        self.connect("realize", self.__on_realize)
        self.set_size_request(1920, 1080) # Hardcoded for this example

        self.__playbin = Gst.ElementFactory.make("playbin", "player")
        self.__bus = self.__playbin.get_bus()
        self.__bus.add_signal_watch()
        self.__bus.connect("message::eos", self.__on_video_end)
        self.__bus.enable_sync_message_emission()
        self.__bus.connect("sync-message::element", self.__on_sync_message)
        self.__playbin.set_property("uri", self.__video_uri)

    def __on_realize(self, widget: Gtk.Window, data=None) -> None:
        window = widget.get_window()
        self.__xid = window.get_xid()

    def __on_sync_message(self, bus: Gst.Bus, message: Gst.Message) -> None:
        if message.get_structure().get_name() == "prepare-window-handle":
            image_sink = message.src
            image_sink.set_property("force-aspect-ratio", True)
            image_sink.set_window_handle(self.__xid)

    def __on_video_end(self, bus: Gst.Bus, message: Gst.Message) -> None:
        if self.__loop:
            self.__playbin.set_state(Gst.State.NULL)
            self.__playbin.set_state(Gst.State.PLAYING)

    def play(self) -> None:
        if self.__playbin.get_state(0).state != Gst.State.PLAYING:
            self.__playbin.set_state(Gst.State.PLAYING)

main.py:

#!/usr/bin/python3
from video_player import VideoPlayer
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk

window = Gtk.Window()
video_player = VideoPlayer("test_video.mp4", loop=True)

window.add(video_player)
window.fullscreen()
window.show_all()

video_player.play()

Gtk.main()

This answer provides an example that uses VLC; This was accepted by the author of the question (see comments) - GStreamer on Raspberry Pi 4 and other similar SOCs is often times laggy and a soft solution, without starting to modify the Gstreamer library, is probably not going to help the OP.

Please notice that the code has been inspired by https://www.codementor.io/@princerapa/python-media-player-vlc-gtk-favehuy2b but has been modified to accommodate for your needs.

The required change to make the video loop, which is not provided in the aforementioned link is passing the argument '--input-repeat=-1' to the vlcinstance.

Install dependencies (this assumes you already have gtk installed)

pip install python-vlc

Your code:

import sys
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
gi.require_version('GdkX11', '3.0')
from gi.repository import GdkX11

import vlc

MRL = ""  # File to play
WIDTH = 300
HEIGHT = 300

class ApplicationWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="Python-Vlc Media Player")
        self.player_paused=False
        self.is_player_active = False
        self.connect("destroy",Gtk.main_quit)
           
    def show(self):
        self.show_all()
        
    def setup_objects_and_events(self):
        self.playback_button = Gtk.Button()
        self.stop_button = Gtk.Button()
        
        self.play_image = Gtk.Image.new_from_icon_name(
                "gtk-media-play",
                Gtk.IconSize.MENU
            )
        self.pause_image = Gtk.Image.new_from_icon_name(
                "gtk-media-pause",
                Gtk.IconSize.MENU
            )
        self.stop_image = Gtk.Image.new_from_icon_name(
                "gtk-media-stop",
                Gtk.IconSize.MENU
            )
        
        self.playback_button.set_image(self.play_image)
        self.stop_button.set_image(self.stop_image)
        
        self.playback_button.connect("clicked", self.toggle_player_playback)
        self.stop_button.connect("clicked", self.stop_player)
        
        self.draw_area = Gtk.DrawingArea()
        self.draw_area.set_size_request(WIDTH,HEIGHT)
        
        self.draw_area.connect("realize",self._realized)
        
        self.hbox = Gtk.Box(spacing=6)
        self.hbox.pack_start(self.playback_button, True, True, 0)
        self.hbox.pack_start(self.stop_button, True, True, 0)
        
        self.vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.add(self.vbox)
        self.vbox.pack_start(self.draw_area, True, True, 0)
        self.vbox.pack_start(self.hbox, False, False, 0)
        
    def stop_player(self, widget, data=None):
        self.player.stop()
        self.is_player_active = False
        self.playback_button.set_image(self.play_image)
        
    def toggle_player_playback(self, widget, data=None):

        """
        Handler for Player's Playback Button (Play/Pause).
        """

        if self.is_player_active == False and self.player_paused == False:
            self.player.play()
            self.playback_button.set_image(self.pause_image)
            self.is_player_active = True

        elif self.is_player_active == True and self.player_paused == True:
            self.player.play()
            self.playback_button.set_image(self.pause_image)
            self.player_paused = False

        elif self.is_player_active == True and self.player_paused == False:
            self.player.pause()
            self.playback_button.set_image(self.play_image)
            self.player_paused = True
        else:
            pass
        
    def _realized(self, widget, data=None):
        self.vlcInstance = vlc.Instance("--no-xlib", "--input-repeat=-1")
        self.player = self.vlcInstance.media_player_new()
        win_id = widget.get_window().get_xid()
        self.player.set_xwindow(win_id)
        self.player.set_mrl(MRL)
        self.player.play()
        self.playback_button.set_image(self.pause_image)
        self.is_player_active = True

if __name__ == '__main__':
    if not sys.argv[1:]:
       print("Exiting \nMust provide the MRL.")
       sys.exit(1)
    if len(sys.argv[1:]) == 1:
        MRL = sys.argv[1]
        window = ApplicationWindow()
        window.setup_objects_and_events()
        window.show()
        Gtk.main()
        window.player.stop()
        window.vlcInstance.release()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM