简体   繁体   English

使Python中的GStreamer视频/音频流畅循环

[英]Making GStreamer video/audio in Python smooth and loop

I'm trying to use GStreamer to loop MPEG-4 files smoothly.我正在尝试使用 GStreamer 平滑地循环播放 MPEG-4 文件。 I want to play the video and audio if there is any and loop the playback.我想播放视频和音频(如果有的话)并循环播放。 My app uses GTK for UI.我的应用程序使用 GTK 作为 UI。

Right now I have three problems:现在我遇到三个问题:

  1. The video playback is choppy/jerky on the Raspberry Pi 4, I'm running it on. Raspberry Pi 4 上的视频播放不稳定/不稳定,我正在运行它。 By choppy/jerky, I mean that every ~1-2 seconds, playback freezes for some fraction of a second.断断续续/生涩,我的意思是每隔约 1-2 秒,播放会冻结几分之一秒。 When playing the same video in the VLC app, it is smooth.在 VLC 应用程序中播放同一视频时,它很流畅。
  2. Audio is not played.不播放音频。 Again, when played in VLC, the audio is there as expected.同样,在 VLC 中播放时,音频如预期的那样存在。 It was my understanding that playbin elements automatically play both audio and video.据我了解, playbin元素会自动播放音频和视频。
  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.到达视频结尾时,最后一帧会冻结 1-2 秒,然后视频会再次从第一帧开始播放。

I currently have the following code.我目前有以下代码。


video_player.py: 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;这个答案提供了一个使用 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.问题的作者接受了这一点(请参阅评论)-Raspberry Pi 4 和其他类似 SOC 上的 GStreamer 通常会出现滞后和软解决方案,如果不开始修改 Gstreamer 库,可能不会帮助 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.请注意,该代码的灵感来自https://www.codementor.io/@princerapa/python-media-player-vlc-gtk-favehuy2b ,但已根据您的需要进行了修改。

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.进行视频循环所需的更改(上述链接中未提供)是将参数“--input-repeat=-1”传递给 vlcinstance。

Install dependencies (this assumes you already have gtk installed)安装依赖项(假设您已经安装了 gtk)

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()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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