summaryrefslogtreecommitdiff
path: root/docs/random/wtay/caps-negociation
blob: 1207cd9751fcb581e2ddcc22178a60d90a960976 (plain)
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
caps negotiation
================

1) purpose
----------

The pads expose the media types they can handle using a mime
type and a set of properties. Before the pad is created or
used to pass buffers, we only know the global 'range' of media
data this pad can accept. When the element has had a chance to
look at the media data, only then it knows the exact values of the 
properties.

example1:
!
! The mp3 decoder exposes the capabilities of its src pad
! with the following caps:
!
!     'mpg123_src':
!       MIME type: 'audio/raw':
!       format: Integer: 16
!       depth: Integer: 16
!       rate: Integer range: 11025 - 48000
!       channels: Integer range: 1 - 2

as you can see in example1, the padtemplate has both a range
(for the audio rate) and a list (for the number of channels)
for its properties.

only when the mpg123 element has decoded the first mpeg audio
header, it knows the exact values of the rate and channels
properties.

suppose that we want to connect this src pad to the sink pad
of an audiosink with the properties given in example2:

example2:
!
!     'audiosink_sink':
!       MIME type: 'audio/raw':
!       format: Integer: 16
!       depth: List:
!         Integer: 8
!         Integer: 16
!       rate: Integer range: 8000 - 44000
!       channels: Integer range: 1 - 2

we can see that connecting the mpg123 src pad with the
audiosinks sink pad can cause a potential problem with the
rate property.

When the mpg123 decoder decides to output raw audio with a
48000Hz samplerate, the audiosink will not be able to handle
it. The conservative approach would be to disallow the connection
between the two incompatible pads. This rules out any potential
problems but severely limits the amount of possible connections
between the elements. 

Another approach would be to allow the connection (and mark it
as dangerous) and let the two elements figure out a suitable
media type at runtime. This procedure is called caps negotiation.


2) a bit of history
-------------------

The typing of the data that was attached to a buffer used to be
done using GstMeta* (and it still is as of 11 feb 2001). With
the new GstCaps and GstProps system this typing is gradually moved
to the pads and to the padtemplates. This has several advantages:

  - the typing of the data tends to be static. The type of media
    doesn't change for every buffer.

  - Moving the typing up to the pad(templates) allows us to save
    them into the registry and allows us to figure out what pads
    are compatible.

  - the current metadata implementation needs header files. this may
    change when we also use properties for metadata.

example3:
!
! This is the current GstMeta structure that travels with audio buffers
!
!  struct _MetaAudioRaw {
!    GstMeta meta;
!
!    /* formatting information */
!    gint format;
!    gint channels;
!    gint frequency;
!    gint bps;
!  };
	    

The question still remains what purpose the metadata will serve
now that we expose the media type in the pads. Some possibilities:

  - interesting information, not describing the data itself but the
    context in which the data was generated (suggested buffer size,
    timestamps, etc...)

  - user app metadata. 

In this proposal we also assume that the current use of metadata using
GstMeta is deprecated and that we move this information to the properties
of the pads.


3) the pad/padtemplates caps
----------------------------

All elements have to provide a padtemplate for their pads.

The padtemplates provide a range of possible media types this pad can
src/sink. the main purpose for the padtemplates is to allow a
rough guess at which pads are compatible before even a single buffer
has been processed by the element.

pads are usually created from the templates. When the pad is created
it has no GstCaps* attached to it yet. The possible caps this pad
can have is exposed in the padtemplate. The caps are filled in by
the element when it knows the values for the caps.


4) the connect function
-----------------------

when two pads are connected the following steps will take
placei (not sure, FIXME): 

 - if both pads have caps, the caps are checked. If the caps
   are incompatible, the padtemplates are checked, if they
   are compatible, caps negotiation is performed.

 - if one of the pads has caps, the caps is checked against
   the padtemplate of the peer pad. If they are incompatible,
   the padtemplates are compared, if they are incompatible,
   caps negotiation is performed.
   
 - if none of the pads have caps, the padtemplates are checked, 
   if they are incompatible, a warning is issued.
   

5) when the element knows the media type it is handling
-------------------------------------------------------

When the element has received its first buffer it will know
the media type it is handling by inspecting the buffer.

before pushing the data out to its peer element(s), the element
will set its src pad with the appropriate caps and properties.
These caps must follow the following rules:

  - the caps must be compatible with the padtemplates of this 
    pad.

  - the caps cannot contain ranges or lists.

when the element wants to change the caps of a pad, it has to 
perform gst_pad_renegotiate (GstPad *pad). this will trigger
the caps negotiation procedure.

this will trigger the class method of the pad and calls the pads
gst_pad_negotiate function:

  GstCaps *gst_pad_negotiate (GstPad *pad, GstCaps *caps, guint count);

This function takes a GstCaps *structure as an argument (typically the
current caps of the pad) and a negotiation counter. this counter can be
used to keep track of the negotiation process.

The pad then creates a new caps structure with the desired caps.
If the caps are accepted, it just returns the provided input caps. the
_renegotiate function will set the caps of both pads whenever the
input caps are the same (pointer wise) as the input caps.

the caps structure is checked against the padtemplate of the peer pad,
if it is incompatible the gst_pad_negotiate function is called again
and the element is supposed to create another caps structure.

the gst_pad_renegotiate function then calls the gst_pad_negotiate
function of the peer pad with the new caps as the argument. The peer
pad can adjust or create a new caps if it doesn't accept it. 

the caps structure keeps on bouncing between the two pads until one
of the pads negotiation functions returns the caps unmodified. 

The element can also return a NULL pointer if it has run out of
options for the caps structure. When this happens, both pads are set
the the NULL caps again and the pad connnection is broken.

The negotiation process is stopped after a fixed number of tries,
when the counter has reached some limit. This limit is typically
checked by the pads negotiate function.


6) caps negotiation function
----------------------------

the negotiate function of a pad is called whenever the pad or
peer pad has performed _renegotiate.

example5:
!
! this is the caps negotiation function implemented by an element on
! one of its sink pads.
!
!  static GstCaps*
!  gst_pad_negotiate (GstPad *pad, GstCaps *caps, guint counter)
!  {
!    /* we don't accept anything else than audio/raw */
!    if (strcmp (gst_caps_get_mime (caps), "audio/raw"))
!      return NULL;
!
!    if (gst_caps_get_int_prop (caps, "format") != AFMT_S16_LE)
!      return NULL;
!      
!    /* we accept everything else */
!    return caps;
!  }

When the negotiate function returns NULL (it does not accept the
specified caps of the peer pad), the negotiation process is stopped.



APPENDIX A: use cases
=====================

1) mpg123 src!sink audiosink
----------------------------

When the pads are connected the padtemplates are checked and it 
turns out that the pads might be incompatible (mpg123 can do
48000Hz while audiosink can do 44000Hz). Nothing happens at 
connect time except for the user app that can mark this connection
as possibly dangerous and keep some spare elements ready for when 
the pads turn out to be incompatible.

both elements start out with no caps at all (NULL). mpg123 wants
to output a buffer with specific properties. It calls 
gst_pad_renegotiate (mpg123->srcpad). 

The _renegotiate functions calls the negotiate function of the
mpg123->srcpad. the negotiate function would look like this:


/*
 * The mpg123 element cannot convert the decoded type into something
 * else so it has to force the caps of the src pad into the specific
 * type as defined by the mp3.
 */
static GstCaps*
gst_mpeg123_src_negotiate (GstPad *pad, GstCaps *caps, guint counter)
{
  GstMpg123 *mpg123;

  mpg123 = GST_MPG123 (gst_pad_get_parent (pad));

  /* we got caps in, check them */
  if (caps != NULL) {
    if (!strcmp (gst_caps_get_mime (caps), "audio/raw") &&
        (gst_caps_get_int_prop (caps, "format") == AFMT_S16_LE) &&
        (gst_caps_get_int_prop (caps, "depth") == 16) &&
        (gst_caps_get_int_prop (caps, "rate") == mpg123->rate) &&
        (gst_caps_get_int_prop (caps, "channels") == mpg123->channels)) {
      return caps;
    }
  }
  /* we didn't get caps, so we decide */
  else if (counter != 2) {
    GstCaps *new;

    /* fill in our desired caps */
    new = gst_caps_new_with_props (
            "src_caps",                       /* name */
            "audio/raw",                      /* mime */
            gst_props_new (
              "format",   GST_PROPS_INT (AFMT_S16_LE),
              "depth",    GST_PROPS_INT (16),
              "rate",     GST_PROPS_INT (mpg123->rate),
              "channels", GST_PROPS_INT (mpg123->channels),
              NULL
            )
          );
    return caps;
  }
  /* too many attempts at nogotiation, bail out */
  return NULL;
}


The audiosink pad negotiate function would look like this:

/*
 * The audiosink has a wide range of possible parameters for
 * its sink pad, based on the audio card capabilities and 
 * possibly the element configuration.
 * we assume the audiosink element can be both the initiator of 
 * the negotiations and the negotiated one.
 */
static GstCaps*
gst_audiosink_sink_negotiate (GstPad *pad, GstCaps *caps, guint counter)
{
  GstAudiosink *audiosink;
  gboolean accepted = TRUE;

  audiosink = GST_AUDIOSINK (gst_pad_get_parent (pad));

  /* we got caps in, we know they will match the padtemplate */
  if (caps != NULL) {
    return caps;
  }
  /* we didn't get caps, so we decide */
  else if (counter != 2) {
    GstCaps *new;

    /* fill in our desired caps */
    new = gst_caps_new_with_props (
            "sink_caps",                      /* name */
            "audio/raw",                      /* mime */
            gst_props_new (
              "format",   GST_PROPS_INT (audiosink->format),
              "depth",    GST_PROPS_INT (audiosink->depth),
              "rate",     GST_PROPS_INT (audiosink->rate),
              "channels", GST_PROPS_INT (audiosink->channels),
              NULL
            )
          );
    return caps;
  }
  /* too many attempts at nogotiation, bail out */
  return NULL;
}