1 #!/usr/bin/env python3.4
3 # This file is part of Libre-Streamer.
5 # Libre-Streamer is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
10 # Libre-Streamer is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with Libre-Streamer. If not, see <http://www.gnu.org/licenses/>.
18 # Copyright (c) 2016 David Testé
20 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
23 # - Display the Gst element 'videotestsrc', in case of failure of the pipeline
24 # - Add a checkbox to enable/disable options (storing/streaming - storing only - stream only - etc...)
25 # - Add a function to get the ip address of the camera automatically (see github.com/paulmilliken)
26 # - Create a module for the network configuration (fan/cpu, ifconfig, stream server,etc)
27 # - Generate a log file during runtime. (e.g. this will let you know if the network configuration
28 # and the pipeline construction went well (or not))
29 # - Add an input source choice for the user (camera on IP or webcam)
30 # - Add a time counter
31 # - Add a VU-meter to check if audio feed is emitting signal
32 # - Add a 'CPU load' widget
33 # - Add the FSF logo (need to do some pixel art) as an application icon
34 # - Add the FSF logo inside the streamer use the 'textoverlay' method in ElementFactory.make()
35 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
37 # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
38 # INFO: run the following command in a terminal before launching libre-streamer to get a error log.
39 # GST_DEBUG=4,python:5,gnl*:5 ./libre-streamer.py | tee -a log 2>&1
40 # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
42 __author__
= 'David Testé'
45 __maintainer__
= 'David Testé'
46 __email__
= 'soonum@gnu.org'
47 __status__
= 'Prototype'
51 from time
import time
, gmtime
, strftime
54 gi
.require_version('Gtk', '3.0')
55 from gi
.repository
import Gtk
56 gi
.require_version('Gst', '1.0')
57 from gi
.repository
import Gst
58 from gi
.repository
import GdkX11
59 from gi
.repository
import GstVideo
60 from gi
.repository
import GObject
64 formatted_date
= strftime('%Y_%m_%d', gmtime())
65 metadata
= {'speaker_name':'NC',
70 class Streamgui(object):
76 self
.win
= Gtk
.Window()
77 self
.win
.set_title("Libre-Streamer")
78 self
.win
.connect("delete_event",
79 lambda w
,e
: Gtk
.main_quit())
80 vbox
= Gtk
.VBox(False, 0)
81 vbox_labels
= Gtk
.VBox(False, 0)
82 vbox_entries
= Gtk
.VBox(False, 0)
83 vbox_streaminfo
= Gtk
.VBox(False, 0)
84 vbox_cpuinfo
= Gtk
.VBox(False, 0)
85 hbox
= Gtk
.HBox(False, 0)
86 hbox_time
= Gtk
.HBox(False, 0)
88 self
.videowidget
= Gtk
.DrawingArea()
89 self
.videowidget
.set_size_request(600, 400)
91 self
.baseinfo_label
= Gtk
.Label('Base info: ')
92 self
.baseinfo_entry_label
= Gtk
.Label('LP_' + formatted_date
)
93 self
.speakerinfo_label
= Gtk
.Label('Speaker name: ')
94 self
.speakerinfo_entry
= Gtk
.Entry()
95 self
.sessioninfo_label
= Gtk
.Label('Session name: ')
96 self
.sessioninfo_entry
= Gtk
.Entry()
98 self
.stream_button
= Gtk
.Button("Stream")
99 self
.stream_button
.connect("clicked", self
.on_stream_clicked
)
100 self
.streamtime_label
= Gtk
.Label('Time elapsed ')
101 self
.streamtime_value
= Gtk
.Label('00:00:00')
103 vbox_labels
.pack_start(self
.baseinfo_label
, True, True, 0)
104 vbox_labels
.pack_start(self
.speakerinfo_label
, True, True, 0)
105 vbox_labels
.pack_start(self
.sessioninfo_label
, True, True, 0)
106 vbox_entries
.pack_start(self
.baseinfo_entry_label
, True, True, 0)
107 vbox_entries
.pack_start(self
.speakerinfo_entry
, True, True, 0)
108 vbox_entries
.pack_start(self
.sessioninfo_entry
, True, True, 0)
109 vbox_streaminfo
.pack_start(self
.stream_button
, False, True, 15)
110 hbox_time
.pack_start(self
.streamtime_label
, False, False, 0)
111 hbox_time
.pack_start(self
.streamtime_value
, False, False, 0)
112 vbox_streaminfo
.pack_start(hbox_time
, False, True, 0)
113 hbox
.pack_start(vbox_labels
, False, False, 0)
114 hbox
.pack_start(vbox_entries
, False, False, 0)
115 hbox
.pack_start(vbox_streaminfo
, False, False, 0)
116 vbox
.pack_start(self
.videowidget
, True, True, 0)
117 vbox
.pack_start(hbox
, False, True, 0)
120 self
.win
.set_position(Gtk
.WindowPosition
.CENTER
)
123 self
.xid
= self
.videowidget
.get_property('window').get_xid()
125 self
.pipel
= gstconf
.New_user_pipeline()
127 bus
= gstconf
.get_gstreamer_bus()
128 bus
.connect("sync-message::element", self
.on_sync_message
)
130 def on_sync_message(self
, bus
, message
):
132 if message
.get_structure().get_name() == 'prepare-window-handle':
133 imagesink
= message
.src
134 imagesink
.set_property('force-aspect-ratio', True)
135 imagesink
.set_window_handle(self
.videowidget
.get_property('window').get_xid())
137 def on_stream_clicked(self
, widget
):
139 labelname
= self
.stream_button
.get_label()
140 if labelname
== 'Stream':
141 self
.clean_entry_fields()
142 self
.pipel
.stream_play()
143 ## self.pipel.get_stream_state()
144 self
.stream_button
.set_label('ON AIR')
146 elif labelname
== 'ON AIR':
147 self
.pipel
.stream_stop()
148 self
.stream_button
.set_label('Stream')
149 self
.build_filename()
151 def build_filename(self
):
152 """Get text in entries, check if empty and apply formatting if needed."""
154 base
= self
.baseinfo_entry_label
.get_text()
155 speaker
= self
.speakerinfo_entry
.get_text()
156 speaker
= sep
.join(speaker
.split())
157 session
= self
.sessioninfo_entry
.get_text()
158 session
= sep
.join(session
.split())
159 raw_filename
= base
+ sep
+ speaker
+ sep
+ session
161 has_all_fields
= False
162 while not has_all_fields
:
163 if speaker
and session
:
164 if len(raw_filename
) <= maxlen
:
165 has_all_fields
= True
167 offset
= len(raw_filename
) - maxlen
168 raw_filename
= raw_filename
[:-offset
]
169 has_all_fields
= True
172 # One of the field is empty, open a dialogbox to ask for filling the field
173 self
.pipel
.set_filenames(raw_filename
)
175 def clean_entry_fields(self
):
176 self
.speakerinfo_entry
.set_text('')
177 self
.sessioninfo_entry
.set_text('')
179 ## Use threading module to refresh the time elapsed sinc the begining of the stream??
180 def time_elapsed(self
, widget
):
181 if self
.pipel
.stream_get_state() == 'PLAYING':
185 if __name__
== "__main__":