part-element-sink.txt 7.74 KB
Newer Older
Wim Taymans's avatar
Wim Taymans committed
1 2 3 4 5
Sink elements
-------------

Sink elements consume data. They normally have no source pads.

6
Typical sink elements include:
Wim Taymans's avatar
Wim Taymans committed
7 8 9 10 11 12 13 14

 - audio/video renderers
 - network sinks
 - filesinks

Sinks are harder to construct than other element types as they are 
treated specially by the GStreamer core.

15
state changes
16
~~~~~~~~~~~~~
17 18 19 20 21 22

A sink always returns ASYNC from the state change to PAUSED, this 
includes a state change from READY->PAUSED and PLAYING->PAUSED. The
reason for this is that this way we can detect when the first buffer
or event arrives in the sink when the state change completes.

23 24
A sink should block on the first EOS event or buffer received in the
READY->PAUSED state before commiting the state to PAUSED.
25 26 27 28

FLUSHING events have to be handled out of sync with the buffer flow
and take no part in the preroll procedure.

29
Events other than EOS do not complete the preroll stage.
30

31
sink overview
32
~~~~~~~~~~~~~
33

34 35 36 37 38 39 40
 - TODO: PREROLL_LOCK can be removed and we can safely use the STREAM_LOCK.



  # commit the state. We return TRUE if we can continue
  # streaming, FALSE in the case we go to a READY or NULL state.
  # if we go to PLAYING, we don't need to block on preroll.
41
  commit
42
  {
43
    LOCK
44
    switch (pending)
45
      case PLAYING:
46 47
        need_preroll = FALSE
	break
48
      case PAUSED:
49
	break
50 51
      case READY:
      case NULL:
52
        return FALSE
53
      case VOID:
54 55 56 57 58 59
        return TRUE

    # update state
    state = pending
    next = VOID
    pending = VOID
60
    UNLOCK
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
    return TRUE
  }

  # sync an object. We have to wait for the element to reach
  # the PLAYING state before we can wait on the clock.
  # some items do not need synchronisation (most events) so the
  # get_times method returns FALSE (not syncable)
  # need_preroll indicates that we are not in the PLAYING state
  # and therefore need to commit and potentially block on preroll
  # if our clock_wait got interrupted we commit and block again.
  # The reason for this is that the current item being rendered is
  # not yet finished and we can use that item to finish preroll.
  do_sync (obj)
  {
    # get timing information for this object
    syncable = get_times (obj, &start, &stop)
    if (!syncable)
      return OK;
79
 again:
80 81 82 83 84 85 86 87 88 89
    while (need_preroll)
      if (need_commit)
        need_commit = FALSE
        if (!commit)
          return WRONG_STATE

      if (need_preroll)
        # release PREROLL_LOCK and wait. prerolled can be observed
        # and will be TRUE
        prerolled = TRUE
90
        PREROLL_WAIT (releasing PREROLL_LOCK)
91
        prerolled = FALSE
92 93
        if (flushing)
          return WRONG_STATE
94 95

    if (valid (start || stop))
96
      PREROLL_UNLOCK
97 98
      end_time = stop
      ret = wait_clock (obj,start)
99
      PREROLL_LOCK
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
      if (flushing)
        return WRONG_STATE 
      # if the clock was unscheduled, we redo the
      # preroll
      if (ret == UNSCHEDULED)
        goto again          
  }

  # render a prerollable item (EOS or buffer). It is
  # always called with the PREROLL_LOCK helt.
  render_object (obj)
  {
    ret = do_sync (obj)
    if (ret != OK)
      return ret;

    # preroll and syncing done, now we can render
    render(obj)
  }
                                   | # sinks that sync on buffer contents do like this
                                   | while (more_to_render)
                                   |   ret = render 
                                   |   if (ret == interrupted)
                                   |     prerolled = TRUE
    render (buffer)          ----->|     PREROLL_WAIT (releasing PREROLL_LOCK)
                                   |     prerolled = FALSE
126 127
                                   |     if (flushing)
                                   |       return WRONG_STATE
128 129 130 131 132 133 134 135 136 137 138
                                   | 

  # queue a prerollable item (EOS or buffer). It is
  # always called with the PREROLL_LOCK helt.
  # This function will commit the state when receiving the
  # first prerollable item.
  # items are then added to the rendering queue or rendered
  # right away if no preroll is needed.
  queue (obj, prerollable) 
  {
    if (need_preroll)
139 140 141
      if (prerollable)
        queuelen++

142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
      # first item in the queue while we need preroll
      # will complete state change and call preroll
      if (queuelen == 1)
        preroll (obj)
        if (need_commit)
          need_commit = FALSE
          if (!commit)
            return WRONG_STATE

      # then see if we need more preroll items before we
      # can block
      if (need_preroll)
        if (queuelen <= maxqueue)
          queue.add (obj)
          return OK

158 159 160 161
    # now clear the queue and render each item before
    # rendering the current item.
    while (queue.hasItem)
      render_object (queue.remove())
162 163 164 165 166 167

    render_object (obj)
    queuelen = 0
  }

  # various event functions
168 169
  event
    EOS:
170
      # events must complete preroll too
171 172 173 174
      STREAM_LOCK
      PREROLL_LOCK
      if (flushing)
        return FALSE
175
      ret = queue (event, TRUE)
176 177 178 179
      if (ret == WRONG_STATE)
        return FALSE
      PREROLL_UNLOCK
      STREAM_UNLOCK
180
      break
181
    NEWSEGMENT:
Piotr Fusik's avatar
Piotr Fusik committed
182
      # the newsegment must be used to clip incoming
183 184
      # buffers. Then then go into the queue as non-prerollable
      # items used for syncing the buffers
185 186 187 188 189
      STREAM_LOCK
      PREROLL_LOCK
      if (flushing)
        return FALSE
      set_clip
190 191 192
      ret = queue (event, FALSE)
      if (ret == WRONG_STATE)
        return FALSE
193 194
      PREROLL_UNLOCK
      STREAM_UNLOCK
195
      break
196
    FLUSH_START:
197
      # set flushing and unblock all that is waiting
198 199 200 201 202 203 204 205 206
      event                   ----> subclasses can interrupt render
      PREROLL_LOCK
      flushing = TRUE
      unlock_clock
      PREROLL_SIGNAL
      PREROLL_UNLOCK
      STREAM_LOCK
      lost_state
      STREAM_UNLOCK
207
      break
208
    FLUSH_END:
209
      # unset flushing and clear all data and eos
210 211 212
      STREAM_LOCK
      event 
      PREROLL_LOCK
213 214
      queue.clear
      queuelen = 0
215
      flushing = FALSE
216
      eos = FALSE
217 218
      PREROLL_UNLOCK
      STREAM_UNLOCK
219
      break
220

221 222 223
  # the chain function checks the buffer falls within the
  # configured segment and queues the buffer for preroll and
  # rendering
224 225 226 227 228 229
  chain
    STREAM_LOCK
    PREROLL_LOCK
    if (flushing)
      return WRONG_STATE
    if (clip)
230
      queue (buffer, TRUE)
231 232
    PREROLL_UNLOCK
    STREAM_UNLOCK
233

234 235 236
  state
    switch (transition)
      READY_PAUSED:
237 238 239
        # no datapassing is going on so we always return ASYNC
        ret = ASYNC
	need_commit = TRUE
240 241
        eos = FALSE
        flushing = FALSE
242 243 244
	need_preroll = TRUE
	prerolled = FALSE
        break
245
      PAUSED_PLAYING:
246 247 248
        # we grab the preroll lock. This we can only do if the
	# chain function is either doing some clock sync, we are
	# waiting for preroll or the chain function is not being called.
249 250
        PREROLL_LOCK
	if (prerolled || eos)
251 252 253
          ret = OK
	  need_commit = FALSE
	  need_preroll = FALSE
254 255
	  if (eos)
	    post_eos
256 257
	  else
            PREROLL_SIGNAL
258
	else
259 260 261
	  need_preroll = TRUE
	  need_commit = TRUE
          ret = ASYNC
262
        PREROLL_UNLOCK
263
        break
264 265
      PLAYING_PAUSED:
                           ---> subclass can interrupt render
266 267 268
        # we grab the preroll lock. This we can only do if the
	# chain function is either doing some clock sync
	# or the chain function is not being called. 
269
        PREROLL_LOCK
270
	need_preroll = TRUE
271 272
        unlock_clock
	if (prerolled || eos)
273
          ret = OK
274
	else
275
          ret = ASYNC
276
        PREROLL_UNLOCK
277
        break
278 279
      PAUSED_READY:
                           ---> subclass can interrupt render
280 281 282
        # we grab the preroll lock. Set to flushing and unlock
	# everything. This should exit the chain functions and stop
	# streaming.
283 284 285
        PREROLL_LOCK
        flushing = TRUE
        unlock_clock
286 287
        queue.clear
        queuelen = 0
288
        PREROLL_SIGNAL
289
        ret = OK
290
        PREROLL_UNLOCK
291
        break
Wim Taymans's avatar
Wim Taymans committed
292