Mediaplayer mit GStreamer (Edition gtksink)
| Anke (encarsia)
Inhalt
Mediaplayer mit GStreamer
Im einführenden Artikel zu Mediaplayer mit GStreamer werden Probleme beschrieben, die auf die Verwendung von "xvimagesink" als Videosink zurückzuführen sind.
In diesem Artikel wird als Alternative der Videosink "gtksink" verwendet und nur auf die Unterschiede zu "xvimagesink" eingegangen, da die weitere Vorgehensweise identisch ist.
Installation
Gtksink war ursprünglich Teil der "bad" plugins, befindet sich aber seit der GStreamer-Version 1.14 in den "good" plugins, die im Normalfall bei der Installation von GStreamer mitinstalliert werden.
Eine Ausnahme bildet Ubuntu, wo das Plugin separat im Paket gstreamer1.0-gtk3
(universe) zur Verfügung steht.
Glade
Der Darstellungsbereich der Mediendatei wird durch das gtksink-eigene Widget bereitgestellt. Da dies nicht in Glade verfügbar ist, wird ein leeres Containerwidget (Gtk.Box) benötigt, in das das Widget platziert werden kann.
Python
Videosink einrichten
self.sink = Gst.ElementFactory.make("gtksink")
Widget einrichten
video_widget = self.sink.get_property("widget") builder.get_object("video_box").add(video_widget)
Listings
Python
23_gtksink_simpleplayer.py (Source)
#!/usr/bin/env python # -*- coding: utf-8 -*- import os import time import gi gi.require_version("Gtk", "3.0") gi.require_version("Gst", "1.0") from gi.repository import Gst, Gtk, GLib class GenericException(Exception): pass class Handler: def on_window_destroy(self, *args): Gtk.main_quit() def on_playpause_togglebutton_toggled(self, widget): if app.playpause_button.get_active(): img = Gtk.Image.new_from_icon_name(Gtk.STOCK_MEDIA_PLAY, Gtk.IconSize.BUTTON) widget.set_property("image", img) app.pause() else: img = Gtk.Image.new_from_icon_name(Gtk.STOCK_MEDIA_PAUSE, Gtk.IconSize.BUTTON) widget.set_property("image", img) app.play() def on_forward_clicked(self, widget): app.skip_time() def on_backward_clicked(self, widget): app.skip_time(-1) def on_progress_value_changed(self, widget): app.on_slider_seek def on_vbutton_clicked(self, widget): app.clear_playbin() app.setup_player("mediaplayer.avi") if app.playpause_button.get_active() is True: app.playpause_button.set_active(False) else: app.play() def on_ibutton_clicked(self, widget): app.clear_playbin() app.setup_player("mediaplayer.jpg") app.pause() class GstPlayer: def __init__(self): # init GStreamer Gst.init(None) # setting up builder builder = Gtk.Builder() builder.add_from_file("23_gtksink_player.glade") builder.connect_signals(Handler()) #self.movie_window = builder.get_object("play_here") self.playpause_button = builder.get_object("playpause_togglebutton") self.slider = builder.get_object("progress") self.slider_handler_id = self.slider.connect("value-changed", self.on_slider_seek) # setting up videoplayer self.player = Gst.ElementFactory.make("playbin", "player") self.sink = Gst.ElementFactory.make("gtksink") # setting up media widget video_widget = self.sink.get_property("widget") builder.get_object("video_box").add(video_widget) window = builder.get_object("window") window.show_all() def setup_player(self, f): # file to play must be transmitted as uri uri = "file://" + os.path.abspath(f) self.player.set_property("uri", uri) self.player.set_property("video-sink", self.sink) def play(self): self.is_playing = True self.player.set_state(Gst.State.PLAYING) # starting up a timer to check on the current playback value GLib.timeout_add(1000, self.update_slider) def pause(self): self.is_playing = False self.player.set_state(Gst.State.PAUSED) def current_position(self): status,position = self.player.query_position(Gst.Format.TIME) return position def skip_time(self, direction=1): # skip 20 seconds on forward/backward button app.player.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT, self.current_position() + float(20) * Gst.SECOND * direction, ) def update_slider(self): if not self.is_playing: return False # cancel timeout else: success, self.duration = self.player.query_duration(Gst.Format.TIME) # adjust duration and position relative to absolute scale of 100 self.mult = 100 / (self.duration / Gst.SECOND) if not success: raise GenericException("Couldn't fetch duration") # fetching the position, in nanosecs success, position = self.player.query_position(Gst.Format.TIME) if not success: raise GenericException("Couldn't fetch current position to update slider") # block seek handler so we don't seek when we set_value() self.slider.handler_block(self.slider_handler_id) self.slider.set_value(float(position) / Gst.SECOND * self.mult) self.slider.handler_unblock(self.slider_handler_id) return True # continue calling every x milliseconds def on_slider_seek(self, widget): seek_time = app.slider.get_value() self.player.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT, seek_time * Gst.SECOND / self.mult) def clear_playbin(self): try: self.player.set_state(Gst.State.NULL) except: pass def main(self): Gtk.main() app = GstPlayer() app.main()
Glade
23_gtksink_player.glade (Source)
<?xml version="1.0" encoding="UTF-8"?> <!-- Generated with glade 3.22.1 --> <interface> <requires lib="gtk+" version="3.16"/> <object class="GtkAdjustment" id="adjustment"> <property name="upper">100</property> <property name="step_increment">1</property> <property name="page_increment">10</property> </object> <object class="GtkImage" id="image1"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="stock">gtk-media-rewind</property> </object> <object class="GtkImage" id="image2"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="stock">gtk-media-forward</property> </object> <object class="GtkImage" id="image3"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="stock">gtk-media-pause</property> </object> <object class="GtkApplicationWindow" id="window"> <property name="can_focus">False</property> <signal name="destroy" handler="on_window_destroy" swapped="no"/> <child type="titlebar"> <object class="GtkHeaderBar"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="show_close_button">True</property> <child> <object class="GtkButton" id="vbutton"> <property name="label" translatable="yes">Play video</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <signal name="clicked" handler="on_vbutton_clicked" swapped="no"/> </object> </child> <child> <object class="GtkButton" id="ibutton"> <property name="label" translatable="yes">Show image</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <signal name="clicked" handler="on_ibutton_clicked" swapped="no"/> </object> <packing> <property name="position">1</property> </packing> </child> </object> </child> <child> <object class="GtkBox"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="orientation">vertical</property> <child> <object class="GtkBox" id="video_box"> <property name="height_request">300</property> <property name="visible">True</property> <property name="can_focus">False</property> <property name="orientation">vertical</property> <child> <placeholder/> </child> </object> <packing> <property name="expand">True</property> <property name="fill">True</property> <property name="position">0</property> </packing> </child> <child> <object class="GtkSeparator"> <property name="visible">True</property> <property name="can_focus">False</property> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">1</property> </packing> </child> <child> <object class="GtkBox"> <property name="visible">True</property> <property name="can_focus">False</property> <child> <object class="GtkButtonBox"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="layout_style">start</property> <child> <object class="GtkButton" id="backward"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <property name="image">image1</property> <property name="always_show_image">True</property> <signal name="clicked" handler="on_backward_clicked" swapped="no"/> </object> <packing> <property name="expand">True</property> <property name="fill">True</property> <property name="position">0</property> </packing> </child> <child> <object class="GtkButton" id="forward"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <property name="image">image2</property> <property name="always_show_image">True</property> <signal name="clicked" handler="on_forward_clicked" swapped="no"/> </object> <packing> <property name="expand">True</property> <property name="fill">True</property> <property name="position">1</property> </packing> </child> <child> <object class="GtkToggleButton" id="playpause_togglebutton"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <property name="image">image3</property> <signal name="toggled" handler="on_playpause_togglebutton_toggled" swapped="no"/> </object> <packing> <property name="expand">True</property> <property name="fill">True</property> <property name="position">2</property> </packing> </child> </object> <packing> <property name="expand">False</property> <property name="fill">False</property> <property name="position">0</property> </packing> </child> <child> <object class="GtkScale" id="progress"> <property name="width_request">300</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="halign">center</property> <property name="margin_left">5</property> <property name="margin_right">5</property> <property name="adjustment">adjustment</property> <property name="fill_level">100</property> <property name="round_digits">1</property> <property name="draw_value">False</property> </object> <packing> <property name="expand">False</property> <property name="fill">False</property> <property name="position">1</property> </packing> </child> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">2</property> </packing> </child> </object> </child> </object> </interface>
Comments
Comments powered by Disqus