#!/usr/bin/python import os #the following line needs to be set early enough os.environ['GST_DEBUG_DUMP_DOT_DIR']='/tmp/dot' import sys from os import path import signal import gi gi.require_version('Gtk', '3.0') gi.require_version('Gst', '1.0') gi.require_version('GstVideo', '1.0') #from gi.repository import GObject, Gst, Gtk, Pango from gi.repository import GObject, GLib, Gst, Gtk, Pango # Needed for window.get_xid(), xvimagesink.set_window_handle(), respectively: from gi.repository import GdkX11, GstVideo, Gdk import pprint #import video_widget from time import time, sleep GLib.threads_init() Gst.init(None) Gst.debug_set_active(True) Gst.debug_set_default_threshold(1) class Main(object): """ The Main class is the Gui. It creates an instance of the GstPlayer class and the FileInfo class. It is what the user interacts with and controls what happens. """ key_bindings={ 'fullscreen': 'f', 'play': 'space', 'quit': 'q' } PLAY_IMAGE = Gtk.Image.new_from_stock(Gtk.STOCK_MEDIA_PLAY, Gtk.IconSize.BUTTON) PAUSE_IMAGE = Gtk.Image.new_from_stock(Gtk.STOCK_MEDIA_PAUSE, Gtk.IconSize.BUTTON) def __init__(self, location="/tmp/20131205_155212_mousecam2.avi"): self.accelgroup = None builder = Gtk.Builder() builder.add_from_file("interpolation.glade") #cssProvider = Gtk.CssProvider() #cssProvider.load_from_path('viewer.css') #screen = Gdk.Screen.get_default() #styleContext = Gtk.StyleContext() #styleContext.add_provider_for_screen(screen, cssProvider, Gtk.STYLE_PROVIDER_PRIORITY_USER) windows=[ "window", "pig", "vbox", "hbox", "menu_bar", "play_button", "slider", 'play_menu_item', 'pause_menu_item' ] for w in windows: obj=builder.get_object(w) if hasattr(obj, 'set_name'): obj.set_name(w) #needed to make gtkrc work setattr(self, w, obj) self.videowidget=self.pig self.__is_fullscreen = False self.play_button.set_image(self.PLAY_IMAGE) self.play_button.connect('clicked', self.on_play) self.is_playing=False #self.slider.set_range(0, 100) #self.slider.set_increments(1, 10) builder.connect_signals(self) self.add_key_bindings() self.window.connect('destroy', self.quit) self.window.connect('window-state-event', self.on_window_state_event) self.pipeline = Gst.Pipeline() self.playbin= Gst.parse_bin_from_description('filesrc location={} ! decodebin ! videoconvert ! videoscale name=videoscale ! video/x-raw,width=600,height=300 ! autovideosink'.format(location), False) self.bus = self.pipeline.get_bus() self.bus.add_signal_watch() self.bus.connect('message::eos', self.on_eos) self.bus.connect('message::error', self.on_error) #self.bus.connect('message::buffering', self.on_buffering) self.bus.enable_sync_message_emission() self.bus.connect('sync-message::element', self.on_sync_message) self.bus.connect('message::error', self.on_error_message) self.pipeline.add(self.playbin) self.videoscale=self.playbin.get_by_name("videoscale") def on_eos(self, bus, message): self.playbin.set_state(Gst.State.PAUSED) self.play_button.set_image(self.PLAY_IMAGE) self.is_playing = False self.playbin.seek_simple(gst.Format.TIME, gst.SeekFlags.FLUSH, 0) self.slider.set_value(0) def add_key_bindings(self): if self.accelgroup == None: self.accelgroup = Gtk.AccelGroup() for f,k in self.key_bindings.iteritems(): keyval, modifier = Gtk.accelerator_parse(k) self.accelgroup.connect(keyval, modifier, Gtk.AccelFlags.VISIBLE, getattr(self,'on_'+f)) self.window.add_accel_group(self.accelgroup) def run(self): self.window.show_all() # You need to get the XID after window.show_all(). You shouldn't get it # in the on_sync_message() handler because threading issues will cause # segfaults there. self.xid = self.videowidget.get_property('window').get_xid() print 'XID',self.xid self.pipeline.set_state(Gst.State.PLAYING) if False: self.window.fullscreen() #works! Gtk.main() def on_select_channel(self, treeview, path, col): print "Channel select" iter=treeview.get_model().get_iter(path) id=self.channelList_listStore[iter][2] self.change_channel(id) #pprint.pprint(row) def change_channel(self, id): print "CHANGE Channel", id self.time_ref=time() #self.reset_pipeline() newuri=self.uribase+id self.pipeline.set_state(Gst.State.NULL) src=self.playbin.get_by_name('play') src.set_property('uri', newuri) #sleep(1) self.pipeline.set_state(Gst.State.PLAYING) def quit(self, window): self.pipeline.set_state(Gst.State.NULL) Gtk.main_quit() def on_sync_message(self, bus, msg): s = msg.get_structure() name = msg.get_structure().get_name() #print "SYNC", name if name == 'prepare-window-handle': print('prepare-window-handle') msg.src.set_window_handle(self.xid) def on_error_message(self, bus, msg): #s = msg.get_structure() #name = msg.get_structure().get_name() name = msg.src.get_name() # => "source" if name == 'source': self.pipeline.set_state(Gst.State.NULL) sleep(0.5) self.pipeline.set_state(Gst.State.PLAYING) #name=msg.parse_error() def on_buffering(self, bus, msg): #s = msg.get_structure() #name = msg.get_structure().get_name() print 'buffering', msg.parse_buffering() def on_eos(self, bus, msg): print('on_eos(): seeking to start of video') self.pipeline.seek_simple( Gst.Format.TIME, Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT, 0 ) def on_quit(self,*args): print "QUIT" Gst.debug_bin_to_dot_file(self.pipeline, Gst.DebugGraphDetails.ALL, 'debug-graph') self.playbin.set_state(Gst.State.NULL) self.is_playing = False Gtk.main_quit() def on_step_forward(self, *args): seek_time_secs = self.slider.get_value() seek_time_secs += self.slider.get_adjustment().get_step_increment() seek_time_max = self.slider.get_adjustment().get_upper() if seek_time_secs > seek_time_max: seek_time_secs=seek_time_max self.playbin.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT, seek_time_secs * Gst.SECOND) self.slider.set_value(seek_time_secs) self.update_slider() def on_step_backward(self, *args): seek_time_secs = self.slider.get_value() seek_time_secs -= self.slider.get_adjustment().get_step_increment() if seek_time_secs <0: seek_time_secs=0 self.slider.set_value(seek_time_secs) self.playbin.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT, seek_time_secs * Gst.SECOND) self.update_slider() def on_fast_forward(self, *args): seek_time_secs = self.slider.get_value() seek_time_secs += self.slider.get_adjustment().get_page_increment() seek_time_max = self.slider.get_adjustment().get_upper() if seek_time_secs > seek_time_max: seek_time_secs=seek_time_max self.playbin.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT, seek_time_secs * Gst.SECOND) self.slider.set_value(seek_time_secs) self.update_slider() def on_fast_backward(self, *args): seek_time_secs = self.slider.get_value() seek_time_secs -= self.slider.get_adjustment().get_page_increment() if seek_time_secs <0: seek_time_secs=0 self.slider.set_value(seek_time_secs) self.playbin.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT, seek_time_secs * Gst.SECOND) self.update_slider() def on_play(self, *args): print 'PLAY' if not self.is_playing: print "on_play: PLAY" self.play_menu_item.hide() self.pause_menu_item.show() self.play_button.set_image(self.PLAY_IMAGE) self.is_playing = True self.pipeline.set_state(Gst.State.PLAYING) self.timeout_id = GLib.timeout_add(200, self.update_slider, None) else: print "on_play: PAUSE" self.play_menu_item.show() self.pause_menu_item.hide() self.play_button.set_image(self.PAUSE_IMAGE) self.is_playing = False self.pipeline.set_state(Gst.State.PAUSED) def on_slider_change(self, *args): seek_time_secs = self.slider.get_value() self.playbin.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT, seek_time_secs * Gst.SECOND) def update_slider(self,*args): if not self.is_playing: return False # cancel timeout try: _,nanosecs = self.playbin.query_position(Gst.Format.TIME) _,duration_nanosecs = self.playbin.query_duration(Gst.Format.TIME) # block seek handler so we don't seek when we set_value() #self.slider.handler_block_by_func(self.on_slider_change) #self.slider.set_range(0, float(duration_nanosecs) / Gst.SECOND) self.slider.set_range(0, float(60000000000) / Gst.SECOND) self.slider.set_value(float(nanosecs) / Gst.SECOND) #self.slider.handler_unblock_by_func(self.on_slider_change) except gst.QueryError: # pipeline must not be ready and does not know position pass return True # continue calling every 30 milliseconds def on_error(self, bus, msg): print('on_error():', msg.parse_error()) def on_fullscreen(self,*args): #print "FULL SCREEN",args self.fullscreen_mode() def on_nearest_neighbour(self,*args): #print "FULL SCREEN",args #self.playbin.set_state(Gst.State.Pause self.pipeline.set_state(Gst.State.PAUSED) self.videoscale.set_property("method", 'nearest-neighbour') self.pipeline.set_state(Gst.State.PLAYING) print 'NN XXXX', self.videoscale.get_property("method") def on_lanczos(self,*args): #print "FULL SCREEN",args self.videoscale.set_property("method", 'lanczos') #self.playbin= Gst.parse_bin_from_description('filesrc location={} ! decodebin ! videoconvert ! videoscale method=nearest-neighbour ! video/x-raw,width=600,heignearest-neighbournearest-neighbourht=300 ! autovideosink'.format(location), False) #self.pipeline.add(self.playbin) def on_bilinear(self,*args): print "BILI",args #self.playbin.set_state(Gst.State.Pause #self.videoscale.set_property("method", 'bilinear') self.videoscale.set_property("method", 4) print 'BIL XXXX', self.videoscale.get_property("method") return True #self.playbin= Gst.parse_bin_from_description('filesrc location={} ! decodebin ! videoconvert ! videoscale method=nearest-neighbour ! video/x-raw,width=600,heignearest-neighbournearest-neighbourht=300 ! autovideosink'.format(location), False) #self.pipeline.add(self.playbin) def on_4tap(self,*args): #print "FULL SCREEN",args #self.playbin.set_state(Gst.State.Pause self.videoscale.set_property("method", '4-tap') #self.playbin= Gst.parse_bin_from_description('filesrc location={} ! decodebin ! videoconvert ! videoscale method=nearest-neighbour ! video/x-raw,width=600,heignearest-neighbournearest-neighbourht=300 ! autovideosink'.format(location), False) #self.pipeline.add(self.playbin) def on_window_destroy(self, widget, data=None): print "DESTROY" Gtk.main_quit() def on_window_state_event(self, widget, event): """ Detect window state events to determine whether in fullscreen or not in fullscreen """ self.__is_fullscreen = bool(event.new_window_state & Gdk.WindowState.FULLSCREEN) print "Is fullscreen: ", self.__is_fullscreen def fullscreen_mode(self): """ Called from the on_win_key_press_event method. If the program is in fullscreen this method will unfullscreen it. If the program is not in fullscreen it will set it to fullscreen. This method will also hide the controls while in fullscreen mode. """ if self.__is_fullscreen: self.window.unfullscreen() #self.control_box.show() #self.load_file_button.show() self.menu_bar.show() self.hbox.show() self.play_button.show() else: self.menu_bar.hide() self.hbox.hide() self.window.fullscreen() if __name__ == "__main__": signal.signal(signal.SIGINT, signal.SIG_DFL) m=Main('file://'+ sys.argv[1]) if len(sys.argv)>=2 else Main() m.run()