-
Notifications
You must be signed in to change notification settings - Fork 0
/
basic-tut-3-old.py
131 lines (109 loc) · 6.02 KB
/
basic-tut-3-old.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#!/usr/bin/env python3
import sys
import gi
import logging
gi.require_version("GLib", "2.0")
gi.require_version("GObject", "2.0")
gi.require_version("Gst", "1.0")
from gi.repository import GLib, GObject, Gst
logging.basicConfig(level=logging.DEBUG, format="[%(name)s] [%(levelname)8s] - %(message)s")
logger = logging.getLogger(__name__)
class CustomData:
def __init__(data):
# Initialize GStreamer library
# set up internal path lists, register built-in elements, load standard plugins (GstRegistry)
Gst.init(None)
#Create Elements
#uridecodebin -> Decodes data from a URI into raw media (instantiates source, demuxer, decoder internally)
data.source = Gst.ElementFactory.make("uridecodebin", "source")
#audioconvert -> Convert audio to different formats. Ensures platform interoperability
data.convert = Gst.ElementFactory.make("audioconvert", "convert")
#audioresample -> Useful for converting between different audio sample rates. Again ensures platform interoperability
data.resample = Gst.ElementFactory.make("audioresample", "resample")
#autoaudiosink -> render the audio stream to the audio card
data.sink = Gst.ElementFactory.make("autoaudiosink", "sink")
#Create Empty pipeline
data.pipeline = Gst.Pipeline.new("test-pipeline")
if not data.pipeline or not data.source or not data.convert or not data.resample or not data.sink:
logger.error("Not all elements could be created")
sys.exit(1)
#Add elements to pipeline, the elements are not linked yet!
data.pipeline.add(data.source, data.convert, data.resample, data.sink)
#Link all elements excluding source (which contains demux, hence dynamic)
ret = data.convert.link(data.resample)
ret = ret and data.resample.link(data.sink)
if not ret:
logger.error("Elements could not be linked")
sys.exit(1)
#Set URI to play
data.source.props.uri = "https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm"
#data.source.props.uri = "https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer_gr.srt"
#The uridecodebin element comes with several element signals including `pad-added`
#When uridecodebin(source) creates a source pad, and emits `pad-added` signal, the callback is invoked
#Non-blocking
data.source.connect("pad-added", data.pad_added_handler)
# Goal: start playing pipeline
# element / pipeline states: NULL -> READY -> PAUSED -> PLAYING.
# When a pipeline is moved to PLAYING state, it goes through all these 4 states internally
# NULL : Default start(and end) state. No resources allocated
# READY : Global resources allocated -> opening devices, buffer allocation. Stream is not opened
# PAUSED: Stream opened, but not being processed
# PLAYING: Stream opened and processing happens -> running clock
#In this case, the pipeline first moves from NULL to READY
# pad-added signals are triggered, callback is executed
# Once there is a link success, pipeline moves to PAUSED, then to PLAY
ret = data.pipeline.set_state(Gst.State.PLAYING)
if ret == Gst.StateChangeReturn.FAILURE:
logger.error("Unable to change state of pipeline to playing")
sys.exit(1)
#Listening for messages: Wait for EOS or error
# Why is a bus required? Bus takes care of forwarding messages from pipeline (running in a separate thread) to the application
# Applications can avoid worrying about communicating with streaming threads / the pipeling directly.
# Applications only need to set a message-handler on a bus
# Bus is periodically checked for messages, and callback is called when a message is available
data.bus = data.pipeline.get_bus()
while True:
msg = data.bus.timed_pop_filtered(Gst.CLOCK_TIME_NONE, Gst.MessageType.ERROR | Gst.MessageType.STATE_CHANGED | Gst.MessageType.EOS)
#Parse message
if msg:
if msg.type == Gst.MessageType.ERROR:
err, debug_info = msg.parse_error()
logger.error(f"Error received from element {msg.src.get_name()}: {err.message}")
logger.error(f"Debugging information: {debug_info if debug_info else 'none'}")
break
elif msg.type == Gst.MessageType.EOS:
logger.info("End-Of-Stream reached.")
break
elif msg.type == Gst.MessageType.STATE_CHANGED:
if msg.src == data.pipeline:
old_state, new_state, pending_state = msg.parse_state_changed()
print(f"Pipeline state changed from {Gst.Element.state_get_name(old_state)} to {Gst.Element.state_get_name(new_state)}")
else:
# This should ideally not be reached
logger.error("Unexpected message received.")
break
# callback function
# src is the GstElement which triggered the signal.
# new_pad is the GstPad that has just been added to the src element
# This is the pad to which we want to link.
def pad_added_handler(data, src, new_pad):
sink_pad = data.convert.get_static_pad("sink")
print(f"Received new pad {new_pad.get_name()} from {src.get_name()}")
if sink_pad.is_linked():
print("Already linked")
return
new_pad_caps = new_pad.get_current_caps()
new_pad_struct = new_pad_caps.get_structure(0)
new_pad_type = new_pad_struct.get_name()
if not new_pad_type.startswith("audio/x-raw"):
print("Type not audio.")
return
#Linking
ret = new_pad.link(sink_pad)
if ret == Gst.PadLinkReturn.OK:
print("Link success")
else:
print("Link failure")
return
if __name__ == '__main__':
c = CustomData()