Recent

Author Topic: Porting a Python GStreamer program into Lazarus  (Read 1954 times)

Aruna

  • Hero Member
  • *****
  • Posts: 513
Porting a Python GStreamer program into Lazarus
« on: July 20, 2024, 02:17:30 am »
Hello, I wrote a Python+GStreamer program sometime back. I wanted to try and port it to Lazarus. What would be the best way to proceed? The python code is below.
Code: Python  [Select][+][-]
  1. #!/usr/bin/env python3
  2. import gi
  3. gi.require_version('Gtk', '3.0')
  4. gi.require_version('Gst', '1.0')
  5. from gi.repository import Gtk, Gst
  6. import time
  7.  
  8. class RadioApp(Gtk.Window):
  9.    
  10.     def __init__(self):
  11.         super().__init__()
  12.         self.connect("destroy", Gtk.main_quit)
  13.        
  14.         self.init_ui()
  15.         self.init_gstreamer()
  16.  
  17.     def init_ui(self):
  18.         self.set_size_request(500, 650)
  19.         self.set_position(Gtk.WindowPosition.CENTER)
  20.         self.set_title('Python3 + GTK3 + Gstreamer Demo')
  21.  
  22.         vbox = Gtk.Box()
  23.         vbox.set_orientation(Gtk.Orientation.VERTICAL)
  24.                        
  25.         hbox = Gtk.Box()
  26.         hbox.set_orientation(Gtk.Orientation.HORIZONTAL)
  27.        
  28.         btn_radio = Gtk.Button.new_with_label("Radio")
  29.         btn_atc = Gtk.Button.new_with_label("Air Traffic Control")
  30.         btn_police = Gtk.Button.new_with_label("Police")
  31.        
  32.         btn_atc.connect("clicked", self.load_atc)
  33.         btn_radio.connect("clicked", self.load_radio)
  34.         btn_police.connect("clicked", self.load_police)
  35.        
  36.         hbox.pack_start(btn_radio, False, False, 0)
  37.         hbox.pack_start(btn_atc, False, False, 0)
  38.         hbox.pack_start(btn_police, False, False, 0)
  39.        
  40.         vbox.pack_start(hbox, False, False, 0)
  41.        
  42.         scrolled_window = Gtk.ScrolledWindow()
  43.         scrolled_window.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
  44.         scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
  45.        
  46.         scrolled_window2 = Gtk.ScrolledWindow()
  47.        
  48.        
  49.         vbox.pack_start(scrolled_window, True, True, 0)
  50.         vbox.pack_start(scrolled_window2, True, True, 0)
  51.        
  52.         self.store = Gtk.ListStore(str, str, str)
  53.         self.load_radio(None)  # Load initial radio stations
  54.        
  55.         self.treeview = Gtk.TreeView(model=self.store)
  56.         self.treeview.connect("row-activated", self.on_activated)
  57.         #self.treeview.set_rules_hint(True)
  58.         scrolled_window.add(self.treeview)
  59.        
  60.         renderer_text = Gtk.CellRendererText()
  61.         column = Gtk.TreeViewColumn("Station", renderer_text, text=0)
  62.         column.set_sort_column_id(0)
  63.         self.treeview.append_column(column)
  64.        
  65.         renderer_text = Gtk.CellRendererText()
  66.         column = Gtk.TreeViewColumn("Description", renderer_text, text=1)
  67.         column.set_sort_column_id(1)
  68.         self.treeview.append_column(column)
  69.        
  70.         self.listbox = Gtk.ListBox()
  71.         scrolled_window2.add(self.listbox)
  72.         #vbox.pack_start(self.listbox, True, True, 0)
  73.        
  74.        
  75.         global nowplaying
  76.         nowplaying = Gtk.Label()
  77.         vbox.pack_start(nowplaying, False, False, 0)
  78.        
  79.         global statusbar
  80.         statusbar = Gtk.Statusbar()
  81.         vbox.pack_start(statusbar, False, False, 0)
  82.        
  83.         hbox = Gtk.HBox()
  84.         hbox.pack_end(statusbar, True, True, 0)
  85.        
  86.         global btn
  87.         btn = Gtk.Button.new_with_label("Stop")
  88.         btn.connect("clicked", self.stop)
  89.         hbox.pack_end(btn, False, False, 0)
  90.        
  91.         global btn2
  92.         btn2 = Gtk.Button.new_with_label("Vol -")
  93.         btn2.connect("clicked", self.voldown)
  94.         hbox.pack_end(btn2, False, False, 0)
  95.        
  96.         global btn3
  97.         btn3 = Gtk.Button.new_with_label("Vol +")
  98.         btn3.connect("clicked", self.volup)
  99.         hbox.pack_end(btn3, False, False, 0)      
  100.         #hbox.pack_end(btn_radio, False, False, 0)      
  101.         #hbox.pack_end(btn_atc, False, False, 0)      
  102.         #hbox.pack_end(btn_police, False, False, 0)      
  103.        
  104.        
  105.         vbox.pack_start(hbox, False, False, 0)
  106.        
  107.         self.add(vbox)
  108.         self.show_all()
  109.  
  110.     def init_gstreamer(self):
  111.         Gst.init(None)
  112.         self.playbin = Gst.ElementFactory.make("playbin", "player")
  113.         bus = self.playbin.get_bus()
  114.         bus.add_signal_watch()
  115.         bus.enable_sync_message_emission()
  116.         bus.connect("message::tag", self.bus_message_tag)
  117.         #bus.connect("message", self.bus_message_tag)
  118.  
  119.     def bus_message_tag(self, bus, message):
  120.         taglist = message.parse_tag()                  
  121.         tag_string = taglist.get_string(Gst.TAG_TITLE)        
  122.         print(tag_string[1])
  123.        
  124.         if tag_string[1] is None:
  125.             nowplaying.set_text("No Stream Info")
  126.         else:    
  127.             nowplaying.set_text(tag_string[1])
  128.             label = Gtk.Label()
  129.             label.set_text(tag_string[1] +" : "+ time.ctime())
  130.             self.listbox.add(label)      
  131.             self.show_all()
  132.        
  133.        
  134.         #for key in taglist.keys():
  135.         #    print(key.ljust(15) + " : " + str(taglist[key]))
  136.         #t = taglist.get_string("title")
  137.         #nowplaying.set_text(t)
  138.  
  139.     def voldown(self, widget):
  140.         self.playbin.set_property("volume", self.playbin.get_property("volume") - 0.1)
  141.     def volup(self, widget):
  142.         self.playbin.set_property("volume", self.playbin.get_property("volume") + 0.1)    
  143.         print(self.playbin.get_property("volume"))
  144.  
  145.     def stop(self, widget):
  146.         txt = btn.get_label()
  147.         if txt == "Stop":
  148.             print("Stop selected")
  149.             btn.set_label("Play")
  150.             self.playbin.set_state(Gst.State.NULL)
  151.         elif txt == "Play":
  152.             print("Play selected")
  153.             btn.set_label("Stop")
  154.             self.playbin.set_state(Gst.State.PLAYING)
  155.  
  156.     def on_activated(self, treeView, path, column):
  157.         txt = btn.get_label()
  158.         model = treeView.get_model()
  159.         iter = model.get_iter(path)
  160.         if iter:
  161.             text = model.get_value(iter, 2)
  162.             #statusbar.push(0, text)
  163.            
  164.             self.playbin.set_state(Gst.State.NULL)
  165.             self.playbin.set_property("uri", text)
  166.             self.playbin.set_state(Gst.State.PLAYING)
  167.            
  168.             if txt == "Play":
  169.                 btn.set_label("Stop")
  170.  
  171.     def load_atc(self, widget):
  172.         self.store.clear()
  173.         for station in atc:
  174.             self.store.append(list(station))
  175.  
  176.     def load_radio(self, widget):
  177.         self.store.clear()
  178.         for station in stations:
  179.             self.store.append(list(station))
  180.    
  181.     def load_police(self, widget):
  182.         self.store.clear()
  183.         for station in police:
  184.             self.store.append(list(station))        
  185.  
  186. if __name__ == "__main__":
  187.     stations = [('Deep House Radio','Bucharest - Romania','https://streaming-01.xtservers.com:7000/stream'),
  188.                 ('HIRU FM', 'Sri Lanka', "https://radio.lotustechnologieslk.net:2020/stream/hirufmgarden"),
  189.                 ('SHAA FM','Sri Lanka','https://radio.lotustechnologieslk.net:2020/stream/shaafmgarden'),
  190.                 ('Sirasa FM','Sri Lanka','http://live.trusl.com:1170/;;'),
  191.                 ('Hot 80s Hits', 'ShoutCast', 'http://216.245.210.78:9900/stream'),                
  192.                 ('Heartbeat FM', 'Lovesongs & Heartbreakers', 'http://cast5.magicstreams.gr:8010/stream')]
  193.  
  194.     atc = [('Tokyo International Airport', 'Japan', 'https://s1-bos.liveatc.net/rjtt_app_dep?nocache=2024062002221314785"'),
  195.            ('Kuwait International Airport', 'Kuwait City', 'http://d.liveatc.net/okbk'),
  196.            ('Esenboga International Airport', 'Ankara, Turkey', 'http://d.liveatc.net/ltac'),
  197.            ('TTC','Toronto','https://broadcastify.cdnstream1.com/31629')]
  198.            
  199.     police = [('Bay Area','San Francisco','https://broadcastify.cdnstream1.com/34756')]      
  200.  
  201.     app = RadioApp()
  202.     Gtk.main()
  203.  
« Last Edit: July 20, 2024, 03:06:47 am by Aruna »

cdbc

  • Hero Member
  • *****
  • Posts: 1670
    • http://www.cdbc.dk
Re: Porting a Python GStreamer program into Lazarus
« Reply #1 on: July 20, 2024, 08:10:50 am »
Hi
Hmmm ...the gui-stuff seems pretty straightforward, i.e.: can be easily copied with laz' LCL (note: at least I can read it and make sense of it  ;D )
Regarding the 'gst'-stuff, I think it's a matter of creating a class, that takes care of the gstreamer related stuff and then instantiate that in the place of a 'presenter', so to speak. Let the GStreamer be your 'Model', create a 'Presenter' to handle it and communicate actions/results to/from the LCL-'View'
What python does, is act like MV instead of the full MVP...
Should be doable  8)
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

af0815

  • Hero Member
  • *****
  • Posts: 1379
Re: Porting a Python GStreamer program into Lazarus
« Reply #2 on: July 20, 2024, 08:26:28 am »
The only problem I see, is to find the correct gstreamer bindings for pascal.
regards
Andreas

cdbc

  • Hero Member
  • *****
  • Posts: 1670
    • http://www.cdbc.dk
Re: Porting a Python GStreamer program into Lazarus
« Reply #3 on: July 20, 2024, 09:07:56 am »
Hi
Hmmm, at least C# is one of Anders' languages... /similarities/  %)
https://gstreamer.freedesktop.org/src/gstreamer-sharp/ from here I downloaded the latest version(1.24.5), at the bottom of the page.
In that archive there's a /big/ file called 'gstreamer.api.raw' and when you open it, it's a xml-file with all the enums and functions in libgstreamer.so.1
Maybe one can pick'n choose what to bind, enough to get it to work... *big* file.
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

varianus

  • New Member
  • *
  • Posts: 24
Re: Porting a Python GStreamer program into Lazarus
« Reply #4 on: July 20, 2024, 10:51:38 am »
For my audio player, Ovoplayer, I've manually written bindings for some Gstreamer audio API, https://github.com/varianus/ovoplayer/blob/master/src/import-engines/gstreamer.pas.
It support Gstreamer 0.1 and 1.0 releases.

The bindings is then used in this unit to play audio files, https://github.com/varianus/ovoplayer/blob/master/src/audioengine_gstreamer.pas
This unit is heavy coupled to my player code, but should give a raw idea on how to interact with the API.

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11944
  • FPC developer.
Re: Porting a Python GStreamer program into Lazarus
« Reply #5 on: July 20, 2024, 12:49:00 pm »
afaik gtk and related bindings can be generated using lazarus\tools\gir2pascal.

Aruna

  • Hero Member
  • *****
  • Posts: 513
Re: Porting a Python GStreamer program into Lazarus
« Reply #6 on: July 21, 2024, 12:23:26 am »
Hi
Hmmm ...the gui-stuff seems pretty straightforward, i.e.: can be easily copied with laz' LCL
Yes I am not too worried about constructing the gui. Lazarus will handle it quite easily.

(note: at least I can read it and make sense of it  ;D )

If you are running Linux (what am I saying ofcourse your running Linux) try renaming the file I attached to radio-gtk3.py or a filename of your choice then running Python on the file I just attached. In a shell type
Code: Pascal  [Select][+][-]
  1. python3 radio-gtk.py
and enjoy. The forum seems will not allow me to attach *.py files

Regarding the 'gst'-stuff, I think it's a matter of creating a class, that takes care of the gstreamer related stuff and then instantiate that in the place of a 'presenter', so to speak. Let the GStreamer be your 'Model', create a 'Presenter' to handle it and communicate actions/results to/from the LCL-'View'
What python does, is act like MV instead of the full MVP...
Should be doable  8)
Regards Benny
This was my concern. Has anyone used gstreamer with Lazarus? If so will it be possible to see some code please?

Aruna

  • Hero Member
  • *****
  • Posts: 513
Re: Porting a Python GStreamer program into Lazarus
« Reply #7 on: July 21, 2024, 12:27:18 am »
For my audio player, Ovoplayer, I've manually written bindings for some Gstreamer audio API, https://github.com/varianus/ovoplayer/blob/master/src/import-engines/gstreamer.pas.
It support Gstreamer 0.1 and 1.0 releases.

The bindings is then used in this unit to play audio files, https://github.com/varianus/ovoplayer/blob/master/src/audioengine_gstreamer.pas
This unit is heavy coupled to my player code, but should give a raw idea on how to interact with the API.
Wow, this will really help. Thank you. How on earth did you figure out how to write that API?  :o

Aruna

  • Hero Member
  • *****
  • Posts: 513
Re: Porting a Python GStreamer program into Lazarus
« Reply #8 on: July 21, 2024, 12:28:11 am »
afaik gtk and related bindings can be generated using lazarus\tools\gir2pascal.
Many thanks. I will read up on gir2pascal.

WayneSherman

  • Sr. Member
  • ****
  • Posts: 250
Re: Porting a Python GStreamer program into Lazarus
« Reply #9 on: October 25, 2024, 05:39:53 pm »
For reference:

https://lists.freepascal.org/fpc-pascal/2022-November/060948.html
Quote
Michael Van Canneyt
Tue Nov 1 11:45:14 CET 2022
Hello,

For all people interested in sound/video streaming:

After lots of work, I added the gst unit to the FPC packages.
This unit contains the import declarations for the full libgstreamer-1.0 API.
I didn't translate all C macros, but if you need some which I didn't
translate, let me know and I will add them.

fpc gstreamer package:
https://gitlab.com/freepascal.org/fpc/source/-/tree/main/packages/gstreamer

Michael used h2pas. An alternate for creating gstreamer units is gir2pas:
https://gitlab.com/freepascal.org/lazarus/lazarus/-/tree/main/tools/gir2pascal
https://gitlab.freedesktop.org/gstreamer/gir-files-rs

 

TinyPortal © 2005-2018