1# PyGST Tutorial/States, and the Bus 2 3The material in this section may not seem all that exciting, but serves 4as the foundation for more interesting topics. Hopefully now that I've 5captured your attention with a few sexy examples, we can get through 6this rather dreary subject. In this tutorial, we explore *element 7state*and the *element life-cycle*. We will also examine the *message 8bus* in further detail, and how this relates to tracking state changes. 9Finally, will use this information to polish up our demonstration 10framework. 11 12# States 13 14There are four element states: `gst.STATE_NULL`, `gst.STATE_READY`, 15`gst.STATE_PAUSED`, and `gst.STATE_PLAYING`. The distinction between 16`NULL` and `READY` states deserve some explanation. 17 18`gst.STATE_NULL` 19 20: when state is null resources needed for a gst.Element have not been 21 loaded, these can be libraries, devices etc. This is the first state 22 of any gst.Element. 23 24`gst.STATE_READY` 25 26: when state is ready resources have been allocated, and the 27 gst.Element is ready to be used. 28 29`gst.STATE_PAUSED` 30 31: 32 33`gst.STATE_PLAYING` 34 35: 36 37## The element life-cycle 38 39![](element_lifecycle.png "element_lifecycle.png") 40 41The above illustration summarizes the element life-cycle. 42 43## Dataflow and Blocking 44 45So far, we've only talked about *element state*. However, *pads* also 46have state. Pads can be either *blocked* or *unblocked*. In order for a 47pipeline to transition into the playing state, all the pads of all the 48elements in the pipeline must be in the *unblocked* state. 49 50## Adding a Pause Button 51 52[source for this example](enhanced_demo.py.md) 53 54Let's use what we've learned to modify demo.py to add a *pause* button. 55 56First, add the pause button to the UI layout: 57 58 def createWindow(self): 59 60 ... 61 62 # declare buttons and their associated handlers 63 controls = ( 64 ("play_button", gtk.ToolButton(gtk.STOCK_MEDIA_PLAY), self.onPlay), 65 ("pause_button", gtk.ToolButton(gtk.STOCK_MEDIA_PAUSE), self.onPause), 66 ("stop_button", gtk.ToolButton(gtk.STOCK_MEDIA_STOP), self.onStop), 67 ("quit_button", gtk.ToolButton(gtk.STOCK_QUIT), gtk.main_quit) 68 ) 69 70 ... 71 72Next, define the `onPause()` handler. This handler simply sets the 73pipeline state to the `gst.PAUSED` state. 74 75 def onPause(self, unused_button): 76 self.pipeline.set_state(gst.STATE_PAUSED) 77 78[source for this example](enhanced_demo.py.md) 79 80## Running this Example 81 82This example serves as a drop-in replacement for our original `demo.py`. 83You can run this file stand-alone, but it will be more interesting to 84save it as `demo.py` and re-run the previous examples. 85 86# The Bus 87 88We have seen the Bus before. Remember this code from the first example? 89 90 # this code receives the messages from the pipeline. if we 91 # need to set X11 id, then we call set_xid 92 def bus_handler(unused_bus, message): 93 if message.type == gst.MESSAGE_ELEMENT: 94 if message.structure.get_name() == 'prepare-xwindow-id': 95 set_xid(w) 96 return gst.BUS_PASS 97 98 # create our pipeline, and connect our bus_handler 99 self.pipeline = gst.Pipeline() 100 bus = self.pipeline.get_bus() 101 bus.set_sync_handler(bus_handler) 102 103The bus is one mechanism that GStreamer pipelines use to communicate 104with the application. Elements can post messages onto the Bus from any 105thread, and the messages will be received by the application's main 106thread. Handling bus messages is central to many aspects of GStreamer 107application development, including tracking state changes. 108 109## Connecting to the Bus 110 111There are three ways to connect to the bus: with a *watch*, a *signal 112watch*, and a *sync handler*. 113 114- A watch is a simple call back, which you register by calling 115 `Bus.add_watch()`. 116- If you need more flexibility, add a signal watch by calling 117 `Bus.add_signal_watch()`. This causes the bus to emit the `message` 118 signal, which you can connect to. The `message` signal is not 119 emitted unless you call `add_signal_watch()` 120- If you want to receive bus messages in the same thread from which 121 they are posted, call `Bus.set_sync_handler`. You should probably 122 avoid this method unless you understand how to to write 123 multi-threaded code. 124 125## Messages 126 127There are many message types, including the `gst.MESSAGE_ELEMENT` shown 128above. Here's a list of some common ones, and what they mean: 129 130 Message Type Meaning 131 ----------------------------- ----------------------------------------------- 132 `gst.MESSAGE_ELEMENT` message type for element-specific information 133 `gst.MESSAGE_EOS` the end of the pipeline has been reached 134 `gst.MESSAGE_ERROR` a pipeline error has occurred 135 `gst.MESSAGE_SEGMENT_DONE` the pipeline has completed a *segment seek* 136 `gst.MESSAGE_STATE_CHANGED` an element's state has changed 137 `gst.MESSAGE_TAG` meta-data has been decoded from the stream 138 139We'll talk more about that last message, `gst.MESSAGE_SEGMENT_DONE`, in 140the next article. 141 142The `gst.Message` object is generic, and information is extracted using 143the `parse_*()` set of functions depending on the message type. For 144example, to parse the `gst.MESSAGE_STATE_CHANGED` message, use the 145`parse_state_changed()` method. You may also need to access the 146message's `structure` attribute directly (as we must to do set the 147`ximagesink` element's `xwindow_id`). 148 149## Providing State-Change Feedback 150 151Let's use what we've learned to provide feedback to the user about state 152changes. We want the sensitivity of the playback controls to reflect the 153current state of the pipeline. 154 155[source for this example](enhanced_demo.py.md) 156 157The following table summarizes the sensitivity that Play, Pause, and 158Stop buttons should have in each of the GStreamer states. 159 160 State Play Button Pause Button Stop Button 161 --------------------- ------------- -------------- ------------- 162 `gst.STATE_NULL` Sensitive Insensitive Insensitive 163 `gst.STATE_READY` Sensitive Insensitive Insensitive 164 `gst.STATE_PAUSED` Sensitive Insensitive Sensitive 165 `gst.STATE_PLAYING` Insensitive Sensitive Sensitive 166 167This is easily translated into an else-if chain. What we will do is 168update the sensitivity of all the buttons according to this chart when 169we get a state-changed message. 170 171 def updateButtons(self, state): 172 if state == gst.STATE_NULL: 173 self.play_button.set_sensitive(True) 174 self.pause_button.set_sensitive(False) 175 self.stop_button.set_sensitive(False) 176 elif state == gst.STATE_READY: 177 self.play_button.set_sensitive(True) 178 self.pause_button.set_sensitive(False) 179 self.stop_button.set_sensitive(False) 180 elif state == gst.STATE_PAUSED: 181 self.play_button.set_sensitive(True) 182 self.pause_button.set_sensitive(False) 183 self.stop_button.set_sensitive(True) 184 elif state == gst.STATE_PLAYING: 185 self.play_button.set_sensitive(False) 186 self.pause_button.set_sensitive(True) 187 self.stop_button.set_sensitive(True) 188 189Now, we just need to look for the `gst.MESSAGE_STATE_CHANGED` message on 190the bus, and call `updateButtons` with the new state. First let's define 191our message handler: 192 193 def messageCb(self, bus, message): 194 if message.type == gst.MESSAGE_STATE_CHANGED: 195 old, new, pending = message.parse_state_changed() 196 self.updateButtons(new) 197 return gst.BUS_PASS 198 199We want to receive these element messages in the main thread, since we 200are using the information to update the UI. Therefore, we modify 201`createPipeline()` to connect to our message handler as a bus watch. 202 203One last detail: update the state of the buttons with the null state in 204`createWindow()` 205 206[source for this example](enhanced_demo.py.md) 207 208## Running this Example 209 210This example serves as a drop-in replacement for our original `demo.py`. 211You can run this file stand-alone, but it will be more interesting to 212save it as `demo.py` and re-run the previous examples. 213 214## Handling Pipeline Errors 215 216Our demonstration framework has not done any error handling to speak of. 217Let's fix that. 218