summaryrefslogtreecommitdiff
path: root/pwg-allocation.md
blob: b59b7866d10d84d78d16eded3414387b60ef04ee (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
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
---
title: Memory allocation
...

# Memory allocation

Memory allocation and management is a very important topic in
multimedia. High definition video uses many megabytes to store one
single frame of video. It is important to reuse the memory when possible
instead of constantly allocating and freeing the memory.

Multimedia systems usually use special purpose chips, such as DSPs or
GPUs to perform the heavy lifting (especially for video). These special
purpose chips have usually strict requirements for the memory that they
can operate on and how the memory is accessed.

This chapter talks about the memory management features that GStreamer
plugins can use. We will first talk about the lowlevel `GstMemory`
object that manages access to a piece of memory. We then continue with
`GstBuffer` that is used to exchange data between plugins (and the
application) and that uses `GstMemory`. We talk about `GstMeta` that can
be placed on buffers to give extra info about the buffer and its memory.
For efficiently managing buffers of the same size, we take a look at
`GstBufferPool`. To conclude this chapter we take a look at the
GST\_QUERY\_ALLOCATION query that is used to negotiate memory management
options between elements.

## GstMemory

`GstMemory` is an object that manages a region of memory. The memory
object points to a region of memory of “maxsize”. The area in this
memory starting at “offset” and for “size” bytes is the accessible
region in the memory. the maxsize of the memory can never be changed
after the object is created, however, the offset and size can be
changed.

### GstAllocator

`GstMemory` objects are created by a `GstAllocator` object. Most
allocators implement the default `gst_allocator_alloc()` method but some
allocator might implement a different method, for example when
additional parameters are needed to allocate the specific memory.

Different allocators exist for, for example, system memory, shared
memory and memory backed by a DMAbuf file descriptor. To implement
support for a new kind of memory type, you must implement a new
allocator object as shown below.

### GstMemory API example

Data access to the memory wrapped by the `GstMemory` object is always
protected with a `gst_memory_map()` and `gst_memory_unmap()` pair. An
access mode (read/write) must be given when mapping memory. The map
function returns a pointer to the valid memory region that can then be
accessed according to the requested access mode.

Below is an example of making a `GstMemory` object and using the
`gst_memory_map()` to access the memory region.

``` c

[...]

  GstMemory *mem;
  GstMapInfo info;
  gint i;

  /* allocate 100 bytes */
  mem = gst_allocator_alloc (NULL, 100, NULL);

  /* get access to the memory in write mode */
  gst_memory_map (mem, &info, GST_MAP_WRITE);

  /* fill with pattern */
  for (i = 0; i < info.size; i++)
    info.data[i] = i;

  /* release memory */
  gst_memory_unmap (mem, &info);

[...]

      
```

### Implementing a GstAllocator

WRITEME

## GstBuffer

A `GstBuffer` is an lightweight object that is passed from an upstream
to a downstream element and contains memory and metadata. It represents
the multimedia content that is pushed or pull downstream by elements.

The buffer contains one or more `GstMemory` objects that represent the
data in the buffer.

Metadata in the buffer consists of:

  - DTS and PTS timestamps. These represent the decoding and
    presentation timestamps of the buffer content and is used by
    synchronizing elements to schedule buffers. Both these timestamps
    can be GST\_CLOCK\_TIME\_NONE when unknown/undefined.

  - The duration of the buffer contents. This duration can be
    GST\_CLOCK\_TIME\_NONE when unknown/undefined.

  - Media specific offsets and offset\_end. For video this is the frame
    number in the stream and for audio the sample number. Other
    definitions for other media exist.

  - Arbitrary structures via `GstMeta`, see below.

### GstBuffer writability

A buffer is writable when the refcount of the object is exactly 1,
meaning that only one object is holding a ref to the buffer. You can
only modify anything in the buffer when the buffer is writable. This
means that you need to call `gst_buffer_make_writable()` before changing
the timestamps, offsets, metadata or adding and removing memory blocks.

### GstBuffer API examples

You can create a buffer with `gst_buffer_new ()` and then add memory
objects to it or you can use a convenience function
`gst_buffer_new_allocate ()` which combines the two. It's also possible
to wrap existing memory with `gst_buffer_new_wrapped_full () ` where you
can give the function to call when the memory should be freed.

You can access the memory of the buffer by getting and mapping the
`GstMemory` objects individually or by using `gst_buffer_map ()`. The
latter merges all the memory into one big block and then gives you a
pointer to this block.

Below is an example of how to create a buffer and access its memory.

``` c

[...]
  GstBuffer *buffer;
  GstMemory *mem;
  GstMapInfo info;

  /* make empty buffer */
  buffer = gst_buffer_new ();

  /* make memory holding 100 bytes */
  mem = gst_allocator_alloc (NULL, 100, NULL);

  /* add the buffer */
  gst_buffer_append_memory (buffer, mem);

[...]

  /* get WRITE access to the memory and fill with 0xff */
  gst_buffer_map (buffer, &info, GST_MAP_WRITE);
  memset (info.data, 0xff, info.size);
  gst_buffer_unmap (buffer, &info);

[...]

  /* free the buffer */
  gst_buffer_unref (buffer);

[...]

      
```

## GstMeta

With the `GstMeta` system you can add arbitrary structures on buffers.
These structures describe extra properties of the buffer such as
cropping, stride, region of interest etc.

The metadata system separates API specification (what the metadata and
its API look like) and the implementation (how it works). This makes it
possible to make different implementations of the same API, for example,
depending on the hardware you are running on.

### GstMeta API example

After allocating a new buffer, you can add metadata to the buffer with
the metadata specific API. This means that you will need to link to the
header file where the metadata is defined to use its API.

By convention, a metadata API with name `FooBar` should provide two
methods, a `gst_buffer_add_foo_bar_meta ()` and a
`gst_buffer_get_foo_bar_meta ()`. Both functions should return a pointer
to a `FooBarMeta` structure that contains the metadata fields. Some of
the `_add_*_meta ()` can have extra parameters that will usually be used
to configure the metadata structure for you.

Let's have a look at the metadata that is used to specify a cropping
region for video frames.

``` c

#include <gst/video/gstvideometa.h>

[...]
  GstVideoCropMeta *meta;

  /* buffer points to a video frame, add some cropping metadata */
  meta = gst_buffer_add_video_crop_meta (buffer);

  /* configure the cropping metadata */
  meta->x = 8;
  meta->y = 8;
  meta->width = 120;
  meta->height = 80;
[...]

      
```

An element can then use the metadata on the buffer when rendering the
frame like this:

``` c

#include <gst/video/gstvideometa.h>

[...]
  GstVideoCropMeta *meta;

  /* buffer points to a video frame, get the cropping metadata */
  meta = gst_buffer_get_video_crop_meta (buffer);

  if (meta) {
    /* render frame with cropping */
    _render_frame_cropped (buffer, meta->x, meta->y, meta->width, meta->height);
  } else {
    /* render frame */
    _render_frame (buffer);
  }
[...]


      
```

### Implementing new GstMeta

In the next sections we show how you can add new metadata to the system
and use it on buffers.

#### Define the metadata API

First we need to define what our API will look like and we will have to
register this API to the system. This is important because this API
definition will be used when elements negotiate what kind of metadata
they will exchange. The API definition also contains arbitrary tags that
give hints about what the metadata contains. This is important when we
see how metadata is preserved when buffers pass through the pipeline.

If you are making a new implementation of an existing API, you can skip
this step and move on to the implementation step.

First we start with making the `my-example-meta.h` header file that will
contain the definition of the API and structure for our metadata.

``` c

#include <gst/gst.h>

typedef struct _MyExampleMeta MyExampleMeta;

struct _MyExampleMeta {
  GstMeta       meta;

  gint          age;
  gchar        *name;
};

GType my_example_meta_api_get_type (void);
#define MY_EXAMPLE_META_API_TYPE (my_example_meta_api_get_type())

#define gst_buffer_get_my_example_meta(b) \
  ((MyExampleMeta*)gst_buffer_get_meta((b),MY_EXAMPLE_META_API_TYPE))

        
```

The metadata API definition consists of the definition of the structure
that holds a gint and a string. The first field in the structure must be
`GstMeta`.

We also define a `my_example_meta_api_get_type ()` function that will
register out metadata API definition. We also define a convenience macro
`gst_buffer_get_my_example_meta ()` that simply finds and returns the
metadata with our new API.

Next let's have a look at how the `my_example_meta_api_get_type ()`
function is implemented in the `my-example-meta.c` file.

``` c

#include "my-example-meta.h"

GType
my_example_meta_api_get_type (void)
{
  static volatile GType type;
  static const gchar *tags[] = { "foo", "bar", NULL };

  if (g_once_init_enter (&type)) {
    GType _type = gst_meta_api_type_register ("MyExampleMetaAPI", tags);
    g_once_init_leave (&type, _type);
  }
  return type;
}

        
```

As you can see, it simply uses the `gst_meta_api_type_register ()`
function to register a name for the api and some tags. The result is a
new pointer GType that defines the newly registered API.

#### Implementing a metadata API

Next we can make an implementation for a registered metadata API GType.
The implementation detail of a metadata API are kept in a `GstMetaInfo`
structure that you will make available to the users of your metadata API
implementation with a `my_example_meta_get_info ()` function and a
convenience `MY_EXAMPLE_META_INFO` macro. You will also make a method to
add your metadata implementation to a `GstBuffer`. Your
`my-example-meta.h` header file will need these additions:

``` c

[...]

/* implementation */
const GstMetaInfo *my_example_meta_get_info (void);
#define MY_EXAMPLE_META_INFO (my_example_meta_get_info())

MyExampleMeta * gst_buffer_add_my_example_meta (GstBuffer      *buffer,
                                                gint            age,
                                                const gchar    *name);

        
```

Let's have a look at how these functions are implemented in the
`my-example-meta.c` file.

``` c

[...]

static gboolean
my_example_meta_init (GstMeta * meta, gpointer params, GstBuffer * buffer)
{
  MyExampleMeta *emeta = (MyExampleMeta *) meta;

  emeta->age = 0;
  emeta->name = NULL;

  return TRUE;
}

static gboolean
my_example_meta_transform (GstBuffer * transbuf, GstMeta * meta,
    GstBuffer * buffer, GQuark type, gpointer data)
{
  MyExampleMeta *emeta = (MyExampleMeta *) meta;

  /* we always copy no matter what transform */
  gst_buffer_add_my_example_meta (transbuf, emeta->age, emeta->name);

  return TRUE;
}

static void
my_example_meta_free (GstMeta * meta, GstBuffer * buffer)
{
  MyExampleMeta *emeta = (MyExampleMeta *) meta;

  g_free (emeta->name);
  emeta->name = NULL;
}

const GstMetaInfo *
my_example_meta_get_info (void)
{
  static const GstMetaInfo *meta_info = NULL;

  if (g_once_init_enter (&meta_info)) {
    const GstMetaInfo *mi = gst_meta_register (MY_EXAMPLE_META_API_TYPE,
        "MyExampleMeta",
        sizeof (MyExampleMeta),
        my_example_meta_init,
        my_example_meta_free,
        my_example_meta_transform);
    g_once_init_leave (&meta_info, mi);
  }
  return meta_info;
}

MyExampleMeta *
gst_buffer_add_my_example_meta (GstBuffer   *buffer,
                                gint         age,
                                const gchar *name)
{
  MyExampleMeta *meta;

  g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL);

  meta = (MyExampleMeta *) gst_buffer_add_meta (buffer,
      MY_EXAMPLE_META_INFO, NULL);

  meta->age = age;
  meta->name = g_strdup (name);

  return meta;
}

        
```

`gst_meta_register ()` registers the implementation details, like the
API that you implement and the size of the metadata structure along with
methods to initialize and free the memory area. You can also implement a
transform function that will be called when a certain transformation
(identified by the quark and quark specific data) is performed on a
buffer.

Lastly, you implement a `gst_buffer_add_*_meta()` that adds the metadata
implementation to a buffer and sets the values of the metadata.

## GstBufferPool

The `GstBufferPool` object provides a convenient base class for managing
lists of reusable buffers. Essential for this object is that all the
buffers have the same properties such as size, padding, metadata and
alignment.

A bufferpool object can be configured to manage a minimum and maximum
amount of buffers of a specific size. A bufferpool can also be
configured to use a specific `GstAllocator` for the memory of the
buffers. There is support in the bufferpool to enable bufferpool
specific options, such as adding `GstMeta` to the buffers in the pool or
such as enabling specific padding on the memory in the buffers.

A Bufferpool can be inactivate and active. In the inactive state, you
can configure the pool. In the active state, you can't change the
configuration anymore but you can acquire and release buffers from/to
the pool.

In the following sections we take a look at how you can use a
bufferpool.

### GstBufferPool API example

Many different bufferpool implementations can exist; they are all
subclasses of the base class `GstBufferPool`. For this example, we will
assume we somehow have access to a bufferpool, either because we created
it ourselves or because we were given one as a result of the ALLOCATION
query as we will see below.

The bufferpool is initially in the inactive state so that we can
configure it. Trying to configure a bufferpool that is not in the
inactive state will fail. Likewise, trying to activate a bufferpool that
is not configured will fail.

``` c

  GstStructure *config;

[...]

  /* get config structure */
  config = gst_buffer_pool_get_config (pool);

  /* set caps, size, minimum and maximum buffers in the pool */
  gst_buffer_pool_config_set_params (config, caps, size, min, max);

  /* configure allocator and parameters */
  gst_buffer_pool_config_set_allocator (config, allocator, &params);

  /* store the updated configuration again */
  gst_buffer_pool_set_config (pool, config);

[...]

      
```

The configuration of the bufferpool is maintained in a generic
`GstStructure` that can be obtained with `gst_buffer_pool_get_config()`.
Convenience methods exist to get and set the configuration options in
this structure. After updating the structure, it is set as the current
configuration in the bufferpool again with
`gst_buffer_pool_set_config()`.

The following options can be configured on a bufferpool:

  - The caps of the buffers to allocate.

  - The size of the buffers. This is the suggested size of the buffers
    in the pool. The pool might decide to allocate larger buffers to add
    padding.

  - The minimum and maximum amount of buffers in the pool. When minimum
    is set to \> 0, the bufferpool will pre-allocate this amount of
    buffers. When maximum is not 0, the bufferpool will allocate up to
    maximum amount of buffers.

  - The allocator and parameters to use. Some bufferpools might ignore
    the allocator and use its internal one.

  - Other arbitrary bufferpool options identified with a string. a
    bufferpool lists the supported options with
    `gst_buffer_pool_get_options()` and you can ask if an option is
    supported with `gst_buffer_pool_has_option()`. The option can be
    enabled by adding it to the configuration structure with
    `gst_buffer_pool_config_add_option ()`. These options are used to
    enable things like letting the pool set metadata on the buffers or
    to add extra configuration options for padding, for example.

After the configuration is set on the bufferpool, the pool can be
activated with `gst_buffer_pool_set_active (pool, TRUE)`. From that
point on you can use `gst_buffer_pool_acquire_buffer ()` to retrieve a
buffer from the pool, like this:

``` c

  [...]

  GstFlowReturn ret;
  GstBuffer *buffer;

  ret = gst_buffer_pool_acquire_buffer (pool, &buffer, NULL);
  if (G_UNLIKELY (ret != GST_FLOW_OK))
    goto pool_failed;

  [...]

      
```

It is important to check the return value of the acquire function
because it is possible that it fails: When your element shuts down, it
will deactivate the bufferpool and then all calls to acquire will return
GST\_FLOW\_FLUSHNG.

All buffers that are acquired from the pool will have their pool member
set to the original pool. When the last ref is decremented on the
buffer, GStreamer will automatically call
`gst_buffer_pool_release_buffer()` to release the buffer back to the
pool. You (or any other downstream element) don't need to know if a
buffer came from a pool, you can just unref it.

### Implementing a new GstBufferPool

WRITEME

## GST\_QUERY\_ALLOCATION

The ALLOCATION query is used to negotiate `GstMeta`, `GstBufferPool` and
`GstAllocator` between elements. Negotiation of the allocation strategy
is always initiated and decided by a srcpad after it has negotiated a
format and before it decides to push buffers. A sinkpad can suggest an
allocation strategy but it is ultimately the source pad that will decide
based on the suggestions of the downstream sink pad.

The source pad will do a GST\_QUERY\_ALLOCATION with the negotiated caps
as a parameter. This is needed so that the downstream element knows what
media type is being handled. A downstream sink pad can answer the
allocation query with the following results:

  - An array of possible `GstBufferPool` suggestions with suggested
    size, minimum and maximum amount of buffers.

  - An array of GstAllocator objects along with suggested allocation
    parameters such as flags, prefix, alignment and padding. These
    allocators can also be configured in a bufferpool when this is
    supported by the bufferpool.

  - An array of supported `GstMeta` implementations along with metadata
    specific parameters. It is important that the upstream element knows
    what kind of metadata is supported downstream before it places that
    metadata on buffers.

When the GST\_QUERY\_ALLOCATION returns, the source pad will select from
the available bufferpools, allocators and metadata how it will allocate
buffers.

### ALLOCATION query example

Below is an example of the ALLOCATION query.

``` c

#include <gst/video/video.h>
#include <gst/video/gstvideometa.h>
#include <gst/video/gstvideopool.h>

  GstCaps *caps;
  GstQuery *query;
  GstStructure *structure;
  GstBufferPool *pool;
  GstStructure *config;
  guint size, min, max;

[...]

  /* find a pool for the negotiated caps now */
  query = gst_query_new_allocation (caps, TRUE);

  if (!gst_pad_peer_query (scope->srcpad, query)) {
    /* query failed, not a problem, we use the query defaults */
  }

  if (gst_query_get_n_allocation_pools (query) > 0) {
    /* we got configuration from our peer, parse them */
    gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
  } else {
    pool = NULL;
    size = 0;
    min = max = 0;
  }

  if (pool == NULL) {
    /* we did not get a pool, make one ourselves then */
    pool = gst_video_buffer_pool_new ();
  }

  config = gst_buffer_pool_get_config (pool);
  gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
  gst_buffer_pool_config_set_params (config, caps, size, min, max);
  gst_buffer_pool_set_config (pool, config);

  /* and activate */
  gst_buffer_pool_set_active (pool, TRUE);

[...]

      
```

This particular implementation will make a custom `GstVideoBufferPool`
object that is specialized in allocating video buffers. You can also
enable the pool to put `GstVideoMeta` metadata on the buffers from the
pool doing `gst_buffer_pool_config_add_option (config,
GST_BUFFER_POOL_OPTION_VIDEO_META)`.

### The ALLOCATION query in base classes

In many baseclasses you will see the following virtual methods for
influencing the allocation strategy:

  - `propose_allocation ()` should suggest allocation parameters for the
    upstream element.

  - `decide_allocation ()` should decide the allocation parameters from
    the suggestions received from downstream.

Implementors of these methods should modify the given `GstQuery` object
by updating the pool options and allocation options.