Media player with VLC
| Anke (encarsia) | Also available in: Deutsch
Contents
Creating a media player with LibVLC
VLC is not just a multimedia player but also a framework with Python bindings available. In this example app a simple media player will be set up via LibVLC (see also the GStreamer mediaplayer article).
LibVLC
The installation of the VLC Python bindings are mandatory. The package is coomonly found under the name python-vlc
.
Glade
display area of the media file: Gtk.DrawingArea widget
control elements: skip for-/backward (Gtk.Button), pause/resume playback (Gtk.Togglebutton)
select media: buttons to show video or image file
manipulate playback: buttons to mute and rotate video
Python
Set up player
The VLC player is initiated when the corresponding widget (Gtk.DrawingArea) is drawn. The realize
is required for that task. This signal in general is available for the widget class.
vlcOptions = "--no-xlib" win_id = widget.get_window().get_xid() setup_player(vlcOptions) vlcInstance = vlc.Instance(options) player = vlcInstance.media_player_new() player.set_xwindow(win_id)
Given options can be regular VLC commandline options. In the example app a click on the "rotate" button turns the video 180 degrees. Therefore the player must be initiated again with the option --video-filter=transform{type=180}
given.
Media playback
Just like the GStreamer player VLC is capable of showing various video, audio and image formats.
player.set_mrl(file_url) #start playback player.play() #pause/resume playback player.pause()
Position scale
The implementation of the progress bar using a slide control is pretty simple.
#retrieve position player.get_position() #define positition player.set_position(val)
Possible values are float numbers between 0 and 1. These functions are quite resource demanding resulting into stuttering playback. In this example the get_position
is avoided by retrieving the slider position instead of the video.
Possibilities and limitations
Working with LibVLC Python bindings is easy and intuitive in contrast to GStreamer. In addition the "headerbar problem" is non-existent.
On the other hand it is not quite minimalistic to resort to a huge and indepentant project. You will have to install VLC and Python bindings instead of just importing the GStreamer module from the GObject Introspection repository.
The overall consumption of resources is bigger.
Listings
Glade
<?xml version="1.0" encoding="UTF-8"?> <!-- Generated with glade 3.20.0 --> <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="width_request">600</property> <property name="height_request">500</property> <property name="can_focus">False</property> <property name="default_width">440</property> <property name="default_height">250</property> <child> <object class="GtkBox" id="box1"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="orientation">vertical</property> <child> <object class="GtkDrawingArea" id="play_here"> <property name="visible">True</property> <property name="can_focus">False</property> <signal name="realize" handler="on_play_here_realize" swapped="no"/> </object> <packing> <property name="expand">True</property> <property name="fill">True</property> <property name="position">0</property> </packing> </child> <child> <object class="GtkSeparator" id="separator1"> <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" id="box3"> <property name="visible">True</property> <property name="can_focus">False</property> <child> <object class="GtkButtonBox" id="buttonbox1"> <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> <property name="always_show_image">True</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> <signal name="change-value" handler="on_progress_change_value" swapped="no"/> </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> <child> <object class="GtkButtonBox" id="buttonbox2"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="homogeneous">True</property> <property name="layout_style">expand</property> <child> <object class="GtkButton" id="vbutton"> <property name="label" translatable="yes">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> <packing> <property name="expand">True</property> <property name="fill">True</property> <property name="position">0</property> </packing> </child> <child> <object class="GtkButton" id="ibutton"> <property name="label" translatable="yes">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="expand">True</property> <property name="fill">True</property> <property name="position">1</property> </packing> </child> <child> <object class="GtkToggleButton" id="mute"> <property name="label" translatable="yes">Mute</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <signal name="toggled" handler="on_mute_toggled" swapped="no"/> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">2</property> </packing> </child> <child> <object class="GtkToggleButton" id="rotate"> <property name="label" translatable="yes">Rotate</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <signal name="toggled" handler="on_rotate_toggled" swapped="no"/> </object> <packing> <property name="expand">True</property> <property name="fill">True</property> <property name="position">3</property> </packing> </child> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">4</property> </packing> </child> </object> </child> <child type="titlebar"> <object class="GtkHeaderBar"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="title">VLC based media player</property> <property name="show_close_button">True</property> <child> <placeholder/> </child> </object> </child> </object> </interface>
Python
20_vlc_simpleplayer.py (Source)
#!/usr/bin/env python # -*- coding: utf-8 -*- import os import subprocess import sys import time import vlc import gi gi.require_version("Gtk", "3.0") from gi.repository import Gtk, Gio, Gdk, GLib class Handler: def on_play_here_realize(self, widget): vlcOptions = "--no-xlib" self.win_id = widget.get_window().get_xid() self.setup_player(vlcOptions) self.player.audio_set_mute(False) self.is_playing = False def on_rotate_toggled(self, widget): pos = self.player.get_position() self.player.stop() self.player.release() self.vlcInstance.release() if widget.get_active(): vlcOptions = "--no-xlib --video-filter=transform{type=180}" else: vlcOptions = "--no-xlib" self.setup_player(vlcOptions) self.player.set_mrl(self.video) self.player.play() self.player.set_position(pos) if not self.is_playing: time.sleep(.05) self.player.pause() def setup_player(self, options): self.vlcInstance = vlc.Instance(options) self.player = self.vlcInstance.media_player_new() self.player.set_xwindow(self.win_id) def on_backward_clicked(self, widget): skip_pos = go.slider.get_value() - 10 if skip_pos < 0: self.player.set_position(0) go.slider.set_value(0) else: self.player.set_position(skip_pos / 100) go.slider.set_value(skip_pos) def on_forward_clicked(self, widget): skip_pos = go.slider.get_value() + 10 if skip_pos > 100: self.player.pause() self.player.set_position(0.99) go.slider.set_value(100) else: self.player.set_position(skip_pos / 100) go.slider.set_value(skip_pos) def on_playpause_togglebutton_toggled(self, widget): if widget.get_active(): img = Gtk.Image.new_from_icon_name(Gtk.STOCK_MEDIA_PLAY, Gtk.IconSize.BUTTON) widget.set_property("image", img) self.is_playing = False else: img = Gtk.Image.new_from_icon_name(Gtk.STOCK_MEDIA_PAUSE, Gtk.IconSize.BUTTON) widget.set_property("image", img) self.is_playing = True self.player.pause() GLib.timeout_add(1000, self.update_slider) def on_vbutton_clicked(self, widget): self.video = "file://" + os.path.abspath("mediaplayer.avi") self.duration = go.get_duration(self.video) self.player.set_mrl(self.video) self.is_playing = True go.slider.set_value(0) go.obj("playpause_togglebutton").set_active(False) go.obj("playpause_togglebutton").set_sensitive(True) go.obj("mute").set_sensitive(True) go.obj("rotate").set_sensitive(True) self.player.play() GLib.timeout_add(1000, self.update_slider) def on_ibutton_clicked(self, widget): image = "file://" + os.path.abspath("mediaplayer.jpg") self.player.set_mrl(image) self.is_playing = False self.player.play() go.obj("playpause_togglebutton").set_sensitive(False) go.obj("mute").set_sensitive(False) go.obj("rotate").set_sensitive(False) def on_mute_toggled(self, widget): if widget.get_active(): widget.set_label("Unmute") else: widget.set_label("Mute") self.player.audio_toggle_mute() def on_progress_change_value(self, widget, scroll, value): self.player.set_position(value / 100) widget.set_value(value) def update_slider(self): if not self.is_playing: return False # cancel timeout else: pos = go.slider.get_value() new_pos = (pos + 100 / self.duration) go.slider.set_value(new_pos) if new_pos > 100: self.is_playing = False return True # continue calling every x milliseconds class VlcPlayer: def __init__(self): self.app = Gtk.Application.new("org.media.player", Gio.ApplicationFlags(0)) self.app.connect("activate", self.on_app_activate) def on_app_activate(self, app): # setting up builder builder = Gtk.Builder() builder.add_from_file("20_vlc_player.glade") builder.connect_signals(Handler()) self.obj = builder.get_object # slider position is float between 0..100 self.slider = self.obj("progress") window = self.obj("window") window.set_application(app) window.show_all() def get_duration(self,video): command = ["ffprobe", "-v", "error", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", video, ] ffprobe_cmd = subprocess.run(command, stdout=subprocess.PIPE) # stdout of subprocess is byte variable, convert into float then into integer return int(float(ffprobe_cmd.stdout.decode())) def run(self, argv): self.app.run(argv) go = VlcPlayer() go.run(None)
Comments
Comments powered by Disqus