Negotiation ----------- Negotiation happens when elements want to push buffers and need to decide on the format. This is called downstream negotiation because the upstream element decides the format for the downstream element. This is the most common case. Negotiation can also happen when a downstream element wants to receive another data format from an upstream element. This is called upstream negotiation. The basics of negotiation are as follows: - GstCaps (see part-caps.txt) are refcounted before they are attached to a buffer to describe the contents of the buffer. It is possible to add a NULL caps to a buffer, this means that the buffer type did not change relative to the previous buffer. If no previous buffer was received by a downstream element, it is free to discard the buffer. - Before receiving a buffer, an element must check if the datatype of the buffer has changed. The element should reconfigure itself to the new format before processing the buffer data. If the data type on the buffer is not acceptable, the element should refuse the buffer by returning an appropriate return value from the chain function. - When requesting a buffer from a bufferpool, the prefered type should be passed to the buffer allocation function. After receiving a buffer from a bufferpool, the datatype should be checked again. - A bufferpool allocation function should try to allocate a buffer of the prefered type. If there is a good reason to choose another type, the alloc function should see if that other type is accepted by the other element, then allocate a buffer of that type and attach the type to the buffer before returning it. The general flow for a source pad starting the negotiation. src sink | | | accepts? | type A |---------------->| | yes | |<----------------| | | get buffer | alloc_buf | from pool |---------------->| with type A | | Create buffer of type A. | | check type |<----------------| and use A | | | push | push buffer |---------------->| Receive type A, reconfigure to with new type| | process type A. | | One possible implementation in pseudo code: [element wants to create a buffer] if not format # see what the peer can do peercaps = gst_pad_peer_get_caps (srcpad) # see what we can do ourcaps = gst_pad_get_caps (srcpad) # get common formats candidates = gst_caps_intersect (peercaps, ourcaps) foreach candidate in candidates # make sure the caps is fixed fixedcaps = gst_pad_fixate_caps (srcpad, candidate) # see if the peer accepts it if gst_pad_peer_accept_caps (srcpad, fixedcaps) # store the caps as the negotiated caps, this will # call the setcaps function on the pad gst_pad_set_caps (srcpad, fixedcaps) break endif done endif # if the type is different, this will call the setcaps function of # the pad. buffer = gst_pad_alloc_buffer (srcpad, 0, size, GST_PAD_CAPS (fixedcaps)); if buffer [fill buffer and push] elseif [no buffer, either no peer or no acceptable format found] endif The general flow for a sink pad starting a renegotiation. src sink | | | accepts? | |<----------------| type B | yes | |---------------->| | | get buffer | alloc_buf | from pool |---------------->| with type A | | Create buffer of new type B. | | check type |<----------------| and | | reconfigure | | | push | push buffer |---------------->| Receive type B, reconfigure to with new type| | process type B. | | Use case: videotestsrc ! xvimagesink 1) Who decides what format to use? - src pad always decides, by convention. sinkpad can suggest a format by putting it high in the getcaps function GstCaps. - since the src decides, it can always choose something that it can do, so this step can only fail if the sinkpad stated it could accept something while later on it couldn't. 2) When does negotiation happen? - before srcpad does a push, it figures out a type as stated in 1), then it calls the pad alloc function with the type. The sinkpad has to create a buffer of that type, src fills the buffer and sends it to sink. - since the sink stated in 1) it could accept the type, it will be able to create a buffer of the type and handle it. - sink checks media type of buffer and configures itself for this type. 3) How can sink request another format? - sink asks if new format is possible for the source. - sink returns buffer with new type in allocfunction. - src receives buffer with new type, reconfigures and pushes. - sink can always select something it can create and handle since it takes the initiative. src should be able to handle the new type since it said it could accept it. videotestsrc ! queue ! xvimagesink - queue implements an allocfunction, proxying all calls to its srcpad peer. - queue proxies all accept and getcaps to the other peer pad. - queue contains buffers with different types.