gstadapter.c 33.6 KB
Newer Older
1
2
/* GStreamer
 * Copyright (C) 2004 Benjamin Otte <otte@gnome.org>
3
 *               2005 Wim Taymans <wim@fluendo.com>
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

21
22
/**
 * SECTION:gstadapter
23
 * @short_description: adapts incoming data on a sink pad into chunks of N bytes
24
 *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
25
26
27
28
 * This class is for elements that receive buffers in an undesired size.
 * While for example raw video contains one image per buffer, the same is not
 * true for a lot of other formats, especially those that come directly from
 * a file. So if you have undefined buffer sizes and require a specific size,
29
30
 * this object is for you.
 *
31
32
33
 * An adapter is created with gst_adapter_new(). It can be freed again with
 * g_object_unref().
 *
34
 * The theory of operation is like this: All buffers received are put
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
35
 * into the adapter using gst_adapter_push() and the data is then read back
36
37
38
 * in chunks of the desired size using gst_adapter_map()/gst_adapter_unmap()
 * and/or gst_adapter_copy(). After the data has been processed, it is freed
 * using gst_adapter_unmap().
39
 *
Wim Taymans's avatar
Wim Taymans committed
40
 * Other methods such as gst_adapter_take() and gst_adapter_take_buffer()
41
 * combine gst_adapter_map() and gst_adapter_unmap() in one method and are
Wim Taymans's avatar
Wim Taymans committed
42
 * potentially more convenient for some use cases.
Wim Taymans's avatar
Wim Taymans committed
43
 *
44
45
 * For example, a sink pad's chain function that needs to pass data to a library
 * in 512-byte chunks could be implemented like this:
46
 * |[
47
48
 * static GstFlowReturn
 * sink_pad_chain (GstPad *pad, GstBuffer *buffer)
49
 * {
50
51
52
 *   MyElement *this;
 *   GstAdapter *adapter;
 *   GstFlowReturn ret = GST_FLOW_OK;
Wim Taymans's avatar
Wim Taymans committed
53
54
 *
 *   // will give the element an extra ref; remember to drop it
55
56
 *   this = MY_ELEMENT (gst_pad_get_parent (pad));
 *   adapter = this->adapter;
Wim Taymans's avatar
Wim Taymans committed
57
 *
58
 *   // put buffer into adapter
59
 *   gst_adapter_push (adapter, buffer);
60
 *   // while we can read out 512 bytes, process them
61
 *   while (gst_adapter_available (adapter) >= 512 && ret == GST_FLOW_OK) {
62
 *     const guint8 *data = gst_adapter_map (adapter, 512);
63
 *     // use flowreturn as an error value
64
 *     ret = my_library_foo (data);
65
66
 *     gst_adapter_unmap (adapter);
 *     gst_adapter_flush (adapter, 512);
67
 *   }
Wim Taymans's avatar
Wim Taymans committed
68
 *
69
70
 *   gst_object_unref (this);
 *   return ret;
71
 * }
72
73
 * ]|
 *
74
75
76
 * For another example, a simple element inside GStreamer that uses GstAdapter
 * is the libvisual element.
 *
77
78
79
80
81
 * An element using GstAdapter in its sink pad chain function should ensure that
 * when the FLUSH_STOP event is received, that any queued data is cleared using
 * gst_adapter_clear(). Data should also be cleared or processed on EOS and
 * when changing state from #GST_STATE_PAUSED to #GST_STATE_READY.
 *
82
83
84
 * Also check the GST_BUFFER_FLAG_DISCONT flag on the buffer. Some elements might
 * need to clear the adapter after a discontinuity.
 *
Wim Taymans's avatar
Wim Taymans committed
85
86
87
88
89
90
91
92
93
 * Since 0.10.24, the adapter will keep track of the timestamps of the buffers
 * that were pushed. The last seen timestamp before the current position
 * can be queried with gst_adapter_prev_timestamp(). This function can
 * optionally return the amount of bytes between the start of the buffer that
 * carried the timestamp and the current adapter position. The distance is
 * useful when dealing with, for example, raw audio samples because it allows
 * you to calculate the timestamp of the current adapter position by using the
 * last seen timestamp and the amount of bytes since.
 *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
94
 * A last thing to note is that while GstAdapter is pretty optimized,
Wim Taymans's avatar
Wim Taymans committed
95
96
97
98
99
100
 * merging buffers still might be an operation that requires a malloc() and
 * memcpy() operation, and these operations are not the fastest. Because of
 * this, some functions like gst_adapter_available_fast() are provided to help
 * speed up such cases should you want to. To avoid repeated memory allocations,
 * gst_adapter_copy() can be used to copy data into a (statically allocated)
 * user provided buffer.
101
102
103
104
 *
 * GstAdapter is not MT safe. All operations on an adapter must be serialized by
 * the caller. This is not normally a problem, however, as the normal use case
 * of GstAdapter is inside one pad's chain function, in which case access is
105
 * serialized via the pad's STREAM_LOCK.
106
 *
Wim Taymans's avatar
Wim Taymans committed
107
 * Note that gst_adapter_push() takes ownership of the buffer passed. Use
108
109
110
 * gst_buffer_ref() before pushing it into the adapter if you still want to
 * access the buffer later. The adapter will never modify the data in the
 * buffer pushed in it.
111
 *
Wim Taymans's avatar
Wim Taymans committed
112
 * Last reviewed on 2009-05-13 (0.10.24).
113
114
 */

115
#include <gst/gst_private.h>
116
#include "gstadapter.h"
117
#include <string.h>
118
119

/* default size for the assembled data buffer */
120
#define DEFAULT_SIZE 4096
121

122
123
static void gst_adapter_flush_unchecked (GstAdapter * adapter, gsize flush);

124
125
126
GST_DEBUG_CATEGORY_STATIC (gst_adapter_debug);
#define GST_CAT_DEFAULT gst_adapter_debug

127
128
129
130
131
#define GST_ADAPTER_GET_PRIVATE(obj)  \
   (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_ADAPTER, GstAdapterPrivate))

struct _GstAdapterPrivate
{
Wim Taymans's avatar
Wim Taymans committed
132
133
134
135
  GstClockTime pts;
  guint64 pts_distance;
  GstClockTime dts;
  guint64 dts_distance;
136

137
  gsize scan_offset;
138
  GSList *scan_entry;
139

Wim Taymans's avatar
Wim Taymans committed
140
  GstMapInfo info;
141
142
};

143
#define _do_init \
144
  GST_DEBUG_CATEGORY_INIT (gst_adapter_debug, "adapter", 0, "object to splice and merge buffers to desired size")
145
146
#define gst_adapter_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstAdapter, gst_adapter, G_TYPE_OBJECT, _do_init);
147

148
149
static void gst_adapter_dispose (GObject * object);
static void gst_adapter_finalize (GObject * object);
150
151
152
153
154
155

static void
gst_adapter_class_init (GstAdapterClass * klass)
{
  GObjectClass *object = G_OBJECT_CLASS (klass);

156
157
  g_type_class_add_private (klass, sizeof (GstAdapterPrivate));

158
159
160
161
162
  object->dispose = gst_adapter_dispose;
  object->finalize = gst_adapter_finalize;
}

static void
163
gst_adapter_init (GstAdapter * adapter)
164
{
165
  adapter->priv = GST_ADAPTER_GET_PRIVATE (adapter);
166
167
  adapter->assembled_data = g_malloc (DEFAULT_SIZE);
  adapter->assembled_size = DEFAULT_SIZE;
Wim Taymans's avatar
Wim Taymans committed
168
169
170
171
  adapter->priv->pts = GST_CLOCK_TIME_NONE;
  adapter->priv->pts_distance = 0;
  adapter->priv->dts = GST_CLOCK_TIME_NONE;
  adapter->priv->dts_distance = 0;
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
}

static void
gst_adapter_dispose (GObject * object)
{
  GstAdapter *adapter = GST_ADAPTER (object);

  gst_adapter_clear (adapter);

  GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
}

static void
gst_adapter_finalize (GObject * object)
{
  GstAdapter *adapter = GST_ADAPTER (object);

  g_free (adapter->assembled_data);

  GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
}

/**
 * gst_adapter_new:
 *
197
 * Creates a new #GstAdapter. Free with g_object_unref().
198
 *
199
 * Returns: (transfer full): a new #GstAdapter
200
201
202
203
 */
GstAdapter *
gst_adapter_new (void)
{
204
  return g_object_newv (GST_TYPE_ADAPTER, 0, NULL);
205
206
207
208
}

/**
 * gst_adapter_clear:
209
 * @adapter: a #GstAdapter
210
 *
211
 * Removes all buffers from @adapter.
212
213
214
215
 */
void
gst_adapter_clear (GstAdapter * adapter)
{
Wim Taymans's avatar
Wim Taymans committed
216
217
  GstAdapterPrivate *priv;

218
219
  g_return_if_fail (GST_IS_ADAPTER (adapter));

Wim Taymans's avatar
Wim Taymans committed
220
221
222
  priv = adapter->priv;

  if (priv->info.memory)
223
224
    gst_adapter_unmap (adapter);

225
226
227
  g_slist_foreach (adapter->buflist, (GFunc) gst_mini_object_unref, NULL);
  g_slist_free (adapter->buflist);
  adapter->buflist = NULL;
228
  adapter->buflist_end = NULL;
229
230
231
  adapter->size = 0;
  adapter->skip = 0;
  adapter->assembled_len = 0;
Wim Taymans's avatar
Wim Taymans committed
232
233
234
235
236
237
  priv->pts = GST_CLOCK_TIME_NONE;
  priv->pts_distance = 0;
  priv->dts = GST_CLOCK_TIME_NONE;
  priv->dts_distance = 0;
  priv->scan_offset = 0;
  priv->scan_entry = NULL;
238
239
240
}

static inline void
Wim Taymans's avatar
Wim Taymans committed
241
update_timestamps (GstAdapter * adapter, GstBuffer * buf)
242
{
Wim Taymans's avatar
Wim Taymans committed
243
244
245
246
247
248
249
250
251
252
253
254
255
  GstClockTime pts, dts;

  pts = GST_BUFFER_PTS (buf);
  if (GST_CLOCK_TIME_IS_VALID (pts)) {
    GST_LOG_OBJECT (adapter, "new pts %" GST_TIME_FORMAT, GST_TIME_ARGS (pts));
    adapter->priv->pts = pts;
    adapter->priv->pts_distance = 0;
  }
  dts = GST_BUFFER_DTS (buf);
  if (GST_CLOCK_TIME_IS_VALID (dts)) {
    GST_LOG_OBJECT (adapter, "new dts %" GST_TIME_FORMAT, GST_TIME_ARGS (dts));
    adapter->priv->dts = dts;
    adapter->priv->dts_distance = 0;
256
  }
257
258
}

Wim Taymans's avatar
Wim Taymans committed
259
260
/* copy data into @dest, skipping @skip bytes from the head buffers */
static void
261
262
copy_into_unchecked (GstAdapter * adapter, guint8 * dest, gsize skip,
    gsize size)
Wim Taymans's avatar
Wim Taymans committed
263
264
265
{
  GSList *g;
  GstBuffer *buf;
266
  gsize bsize, csize;
Wim Taymans's avatar
Wim Taymans committed
267

268
  /* first step, do skipping */
269
270
271
272
273
274
275
  /* we might well be copying where we were scanning */
  if (adapter->priv->scan_entry && (adapter->priv->scan_offset <= skip)) {
    g = adapter->priv->scan_entry;
    skip -= adapter->priv->scan_offset;
  } else {
    g = adapter->buflist;
  }
276
  buf = g->data;
277
  bsize = gst_buffer_get_size (buf);
278
  while (G_UNLIKELY (skip >= bsize)) {
279
    skip -= bsize;
Wim Taymans's avatar
Wim Taymans committed
280
    g = g_slist_next (g);
281
    buf = g->data;
282
    bsize = gst_buffer_get_size (buf);
Wim Taymans's avatar
Wim Taymans committed
283
  }
284
285
  /* copy partial buffer */
  csize = MIN (bsize - skip, size);
Josep Torra's avatar
Josep Torra committed
286
287
  GST_DEBUG ("bsize %" G_GSIZE_FORMAT ", skip %" G_GSIZE_FORMAT ", csize %"
      G_GSIZE_FORMAT, bsize, skip, csize);
288
  gst_buffer_extract (buf, skip, dest, csize);
289
290
291
  size -= csize;
  dest += csize;

Wim Taymans's avatar
Wim Taymans committed
292
  /* second step, copy remainder */
Wim Taymans's avatar
Wim Taymans committed
293
  while (size > 0) {
Wim Taymans's avatar
Wim Taymans committed
294
    g = g_slist_next (g);
295
    buf = g->data;
296
    bsize = gst_buffer_get_size (buf);
297
298
    if (G_LIKELY (bsize > 0)) {
      csize = MIN (bsize, size);
299
      gst_buffer_extract (buf, 0, dest, csize);
300
301
302
      size -= csize;
      dest += csize;
    }
Wim Taymans's avatar
Wim Taymans committed
303
  }
Wim Taymans's avatar
Wim Taymans committed
304
305
}

306
307
308
/**
 * gst_adapter_push:
 * @adapter: a #GstAdapter
309
 * @buf: (transfer full): a #GstBuffer to add to queue in the adapter
310
 *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
311
 * Adds the data from @buf to the data stored inside @adapter and takes
312
313
314
315
316
 * ownership of the buffer.
 */
void
gst_adapter_push (GstAdapter * adapter, GstBuffer * buf)
{
317
  gsize size;
318

319
320
321
  g_return_if_fail (GST_IS_ADAPTER (adapter));
  g_return_if_fail (GST_IS_BUFFER (buf));

322
  size = gst_buffer_get_size (buf);
323
  adapter->size += size;
324

325
326
  /* Note: merging buffers at this point is premature. */
  if (G_UNLIKELY (adapter->buflist == NULL)) {
Wim Taymans's avatar
Wim Taymans committed
327
328
    GST_LOG_OBJECT (adapter, "pushing %p first %" G_GSIZE_FORMAT " bytes",
        buf, size);
329
    adapter->buflist = adapter->buflist_end = g_slist_append (NULL, buf);
Wim Taymans's avatar
Wim Taymans committed
330
    update_timestamps (adapter, buf);
331
  } else {
332
    /* Otherwise append to the end, and advance our end pointer */
Wim Taymans's avatar
Wim Taymans committed
333
334
    GST_LOG_OBJECT (adapter, "pushing %p %" G_GSIZE_FORMAT " bytes at end, "
        "size now %" G_GSIZE_FORMAT, buf, size, adapter->size);
335
336
    adapter->buflist_end = g_slist_append (adapter->buflist_end, buf);
    adapter->buflist_end = g_slist_next (adapter->buflist_end);
337
338
339
  }
}

340
341
342
343
344
345
346
/* Internal method only. Tries to merge buffers at the head of the queue
 * to form a single larger buffer of size 'size'. Only merges buffers that
 * where 'gst_buffer_is_span_fast' returns TRUE.
 *
 * Returns TRUE if it managed to merge anything.
 */
static gboolean
347
gst_adapter_try_to_merge_up (GstAdapter * adapter, gsize size)
348
349
350
{
  GstBuffer *cur, *head;
  GSList *g;
351
  gboolean ret = FALSE;
352
  gsize hsize;
353
354
355
356
357
358
359
360
361
362
363

  g = adapter->buflist;
  if (g == NULL)
    return FALSE;

  head = g->data;
  g = g_slist_next (g);

  /* How large do we want our head buffer? The requested size, plus whatever's
   * been skipped already */
  size += adapter->skip;
364
  hsize = gst_buffer_get_size (head);
365

366
  while (g != NULL && hsize < size) {
367
368
    cur = g->data;
    if (!gst_buffer_is_span_fast (head, cur))
369
      return ret;
370
371

    /* Merge the head buffer and the next in line */
Josep Torra's avatar
Josep Torra committed
372
373
    GST_LOG_OBJECT (adapter, "Merging buffers of size %" G_GSIZE_FORMAT " & %"
        G_GSIZE_FORMAT " in search of target %" G_GSIZE_FORMAT,
374
        hsize, gst_buffer_get_size (cur), size);
375
376

    head = gst_buffer_join (head, cur);
377
    hsize = gst_buffer_get_size (head);
378
    ret = TRUE;
379

Wim Taymans's avatar
Wim Taymans committed
380
    /* Delete the front list item, and store our new buffer in the 2nd list
381
382
383
     * item */
    adapter->buflist = g_slist_delete_link (adapter->buflist, adapter->buflist);
    g->data = head;
384

385
386
387
388
    /* invalidate scan position */
    adapter->priv->scan_offset = 0;
    adapter->priv->scan_entry = NULL;

389
    g = g_slist_next (g);
390
391
  }

392
  return ret;
393
394
}

395
/**
396
 * gst_adapter_map:
397
 * @adapter: a #GstAdapter
398
 * @size: the number of bytes to map/peek
399
400
401
402
403
404
405
406
407
408
 *
 * Gets the first @size bytes stored in the @adapter. The returned pointer is
 * valid until the next function is called on the adapter.
 *
 * Note that setting the returned pointer as the data of a #GstBuffer is
 * incorrect for general-purpose plugins. The reason is that if a downstream
 * element stores the buffer so that it has access to it outside of the bounds
 * of its chain function, the buffer will have an invalid data pointer after
 * your element flushes the bytes. In that case you should use
 * gst_adapter_take(), which returns a freshly-allocated buffer that you can set
Wim Taymans's avatar
Wim Taymans committed
409
 * as #GstBuffer malloc_data or the potentially more performant
410
 * gst_adapter_take_buffer().
411
 *
412
 * Returns #NULL if @size bytes are not available.
413
 *
414
415
 * Returns: (transfer none) (array length=size): a pointer to the first
 *     @size bytes of data, or NULL
416
 */
417
gconstpointer
418
gst_adapter_map (GstAdapter * adapter, gsize size)
419
{
Wim Taymans's avatar
Wim Taymans committed
420
  GstAdapterPrivate *priv;
421
  GstBuffer *cur;
422
423
  gsize skip, csize;
  gsize toreuse, tocopy;
424
  guint8 *data;
425
426
427
428

  g_return_val_if_fail (GST_IS_ADAPTER (adapter), NULL);
  g_return_val_if_fail (size > 0, NULL);

Wim Taymans's avatar
Wim Taymans committed
429
430
431
  priv = adapter->priv;

  if (priv->info.memory)
432
433
    gst_adapter_unmap (adapter);

434
  /* we don't have enough data, return NULL. This is unlikely
435
   * as one usually does an _available() first instead of peeking a
436
437
   * random size. */
  if (G_UNLIKELY (size > adapter->size))
438
439
440
441
442
443
    return NULL;

  /* we have enough assembled data, return it */
  if (adapter->assembled_len >= size)
    return adapter->assembled_data;

444
  do {
445
    cur = adapter->buflist->data;
446
447
448
449
    skip = adapter->skip;

    csize = gst_buffer_get_size (cur);
    if (csize >= size + skip) {
Wim Taymans's avatar
Wim Taymans committed
450
451
452
      if (!gst_buffer_map (cur, &priv->info, GST_MAP_READ))
        return FALSE;

Wim Taymans's avatar
Wim Taymans committed
453
      return (guint8 *) priv->info.data + skip;
454
455
456
457
    }
    /* We may be able to efficiently merge buffers in our pool to
     * gather a big enough chunk to return it from the head buffer directly */
  } while (gst_adapter_try_to_merge_up (adapter, size));
458

459
460
461
462
463
  /* see how much data we can reuse from the assembled memory and how much
   * we need to copy */
  toreuse = adapter->assembled_len;
  tocopy = size - toreuse;

464
  /* Gonna need to copy stuff out */
465
  if (G_UNLIKELY (adapter->assembled_size < size)) {
466
    adapter->assembled_size = (size / DEFAULT_SIZE + 1) * DEFAULT_SIZE;
Josep Torra's avatar
Josep Torra committed
467
    GST_DEBUG_OBJECT (adapter, "resizing internal buffer to %" G_GSIZE_FORMAT,
468
        adapter->assembled_size);
469
470
471
472
473
474
475
476
    if (toreuse == 0) {
      GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "alloc new buffer");
      /* no g_realloc to avoid a memcpy that is not desired here since we are
       * not going to reuse any data here */
      g_free (adapter->assembled_data);
      adapter->assembled_data = g_malloc (adapter->assembled_size);
    } else {
      /* we are going to reuse all data, realloc then */
Josep Torra's avatar
Josep Torra committed
477
478
      GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "reusing %" G_GSIZE_FORMAT " bytes",
          toreuse);
479
480
481
      adapter->assembled_data =
          g_realloc (adapter->assembled_data, adapter->assembled_size);
    }
482
  }
Josep Torra's avatar
Josep Torra committed
483
484
  GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "copy remaining %" G_GSIZE_FORMAT
      " bytes from adapter", tocopy);
485
486
  data = adapter->assembled_data;
  copy_into_unchecked (adapter, data + toreuse, skip + toreuse, tocopy);
487
  adapter->assembled_len = size;
488

489
490
491
  return adapter->assembled_data;
}

492
493
494
495
/**
 * gst_adapter_unmap:
 * @adapter: a #GstAdapter
 *
496
 * Releases the memory obtained with the last gst_adapter_map().
497
498
 */
void
499
gst_adapter_unmap (GstAdapter * adapter)
500
{
Wim Taymans's avatar
Wim Taymans committed
501
502
  GstAdapterPrivate *priv;

503
504
  g_return_if_fail (GST_IS_ADAPTER (adapter));

Wim Taymans's avatar
Wim Taymans committed
505
506
507
  priv = adapter->priv;

  if (priv->info.memory) {
508
    GstBuffer *cur = adapter->buflist->data;
Wim Taymans's avatar
Wim Taymans committed
509
    GST_LOG_OBJECT (adapter, "unmap memory buffer %p", cur);
Wim Taymans's avatar
Wim Taymans committed
510
511
    gst_buffer_unmap (cur, &priv->info);
    priv->info.memory = NULL;
512
513
514
  }
}

515
516
517
/**
 * gst_adapter_copy:
 * @adapter: a #GstAdapter
518
 * @dest: (out caller-allocates) (array length=size): the memory to copy into
519
520
521
522
523
524
525
526
527
528
529
530
531
 * @offset: the bytes offset in the adapter to start from
 * @size: the number of bytes to copy
 *
 * Copies @size bytes of data starting at @offset out of the buffers
 * contained in @GstAdapter into an array @dest provided by the caller.
 *
 * The array @dest should be large enough to contain @size bytes.
 * The user should check that the adapter has (@offset + @size) bytes
 * available before calling this function.
 *
 * Since: 0.10.12
 */
void
532
gst_adapter_copy (GstAdapter * adapter, gpointer dest, gsize offset, gsize size)
533
534
535
{
  g_return_if_fail (GST_IS_ADAPTER (adapter));
  g_return_if_fail (size > 0);
Wim Taymans's avatar
Wim Taymans committed
536
  g_return_if_fail (offset + size <= adapter->size);
537

Wim Taymans's avatar
Wim Taymans committed
538
  copy_into_unchecked (adapter, dest, offset + adapter->skip, size);
539
540
}

541
542
543
/**
 * gst_adapter_flush:
 * @adapter: a #GstAdapter
544
 * @flush: the number of bytes to flush
545
 *
546
547
 * Flushes the first @flush bytes in the @adapter. The caller must ensure that
 * at least this many bytes are available.
548
 *
549
 * See also: gst_adapter_map(), gst_adapter_unmap()
550
 */
551
static void
552
gst_adapter_flush_unchecked (GstAdapter * adapter, gsize flush)
553
554
{
  GstBuffer *cur;
555
  gsize size;
556
557
  GstAdapterPrivate *priv;
  GSList *g;
558

Josep Torra's avatar
Josep Torra committed
559
  GST_LOG_OBJECT (adapter, "flushing %" G_GSIZE_FORMAT " bytes", flush);
560
561
562

  priv = adapter->priv;

Wim Taymans's avatar
Wim Taymans committed
563
564
565
  if (priv->info.memory)
    gst_adapter_unmap (adapter);

566
  /* clear state */
567
568
  adapter->size -= flush;
  adapter->assembled_len = 0;
569
570
571
572

  /* take skip into account */
  flush += adapter->skip;
  /* distance is always at least the amount of skipped bytes */
Wim Taymans's avatar
Wim Taymans committed
573
574
  priv->pts_distance -= adapter->skip;
  priv->dts_distance -= adapter->skip;
575
576
577

  g = adapter->buflist;
  cur = g->data;
578
  size = gst_buffer_get_size (cur);
579
580
581
  while (flush >= size) {
    /* can skip whole buffer */
    GST_LOG_OBJECT (adapter, "flushing out head buffer");
Wim Taymans's avatar
Wim Taymans committed
582
583
    priv->pts_distance += size;
    priv->dts_distance += size;
584
585
586
587
588
589
590
591
    flush -= size;

    gst_buffer_unref (cur);
    g = g_slist_delete_link (g, g);

    if (G_UNLIKELY (g == NULL)) {
      GST_LOG_OBJECT (adapter, "adapter empty now");
      adapter->buflist_end = NULL;
592
593
      break;
    }
Wim Taymans's avatar
Wim Taymans committed
594
    /* there is a new head buffer, update the timestamps */
595
    cur = g->data;
Wim Taymans's avatar
Wim Taymans committed
596
    update_timestamps (adapter, cur);
597
    size = gst_buffer_get_size (cur);
598
  }
599
600
601
  adapter->buflist = g;
  /* account for the remaining bytes */
  adapter->skip = flush;
Wim Taymans's avatar
Wim Taymans committed
602
603
  adapter->priv->pts_distance += flush;
  adapter->priv->dts_distance += flush;
604
605
606
  /* invalidate scan position */
  priv->scan_offset = 0;
  priv->scan_entry = NULL;
607
608
}

609
void
610
gst_adapter_flush (GstAdapter * adapter, gsize flush)
611
612
613
614
615
616
617
618
619
620
621
{
  g_return_if_fail (GST_IS_ADAPTER (adapter));
  g_return_if_fail (flush <= adapter->size);

  /* flushing out 0 bytes will do nothing */
  if (G_UNLIKELY (flush == 0))
    return;

  gst_adapter_flush_unchecked (adapter, flush);
}

Wim Taymans's avatar
Wim Taymans committed
622
623
/* internal function, nbytes should be flushed after calling this function */
static guint8 *
624
gst_adapter_take_internal (GstAdapter * adapter, gsize nbytes)
Wim Taymans's avatar
Wim Taymans committed
625
626
{
  guint8 *data;
627
  gsize toreuse, tocopy;
628
629
630
631
632
633
634
635
636
637
638

  /* see how much data we can reuse from the assembled memory and how much
   * we need to copy */
  toreuse = MIN (nbytes, adapter->assembled_len);
  tocopy = nbytes - toreuse;

  /* find memory to return */
  if (adapter->assembled_size >= nbytes && toreuse > 0) {
    /* we reuse already allocated memory but only when we're going to reuse
     * something from it because else we are worse than the malloc and copy
     * case below */
Josep Torra's avatar
Josep Torra committed
639
640
    GST_LOG_OBJECT (adapter, "reusing %" G_GSIZE_FORMAT " bytes of assembled"
        " data", toreuse);
641
    /* we have enough free space in the assembled array */
Wim Taymans's avatar
Wim Taymans committed
642
    data = adapter->assembled_data;
643
    /* flush after this function should set the assembled_size to 0 */
Wim Taymans's avatar
Wim Taymans committed
644
645
    adapter->assembled_data = g_malloc (adapter->assembled_size);
  } else {
Josep Torra's avatar
Josep Torra committed
646
    GST_LOG_OBJECT (adapter, "allocating %" G_GSIZE_FORMAT " bytes", nbytes);
647
    /* not enough bytes in the assembled array, just allocate new space */
Wim Taymans's avatar
Wim Taymans committed
648
    data = g_malloc (nbytes);
649
650
    /* reuse what we can from the already assembled data */
    if (toreuse) {
Josep Torra's avatar
Josep Torra committed
651
      GST_LOG_OBJECT (adapter, "reusing %" G_GSIZE_FORMAT " bytes", toreuse);
652
653
654
655
656
657
658
      memcpy (data, adapter->assembled_data, toreuse);
    }
  }
  if (tocopy) {
    /* copy the remaining data */
    copy_into_unchecked (adapter, toreuse + data, toreuse + adapter->skip,
        tocopy);
Wim Taymans's avatar
Wim Taymans committed
659
660
661
662
  }
  return data;
}

663
664
665
/**
 * gst_adapter_take:
 * @adapter: a #GstAdapter
666
 * @nbytes: the number of bytes to take
667
668
 *
 * Returns a freshly allocated buffer containing the first @nbytes bytes of the
669
 * @adapter. The returned bytes will be flushed from the adapter.
670
 *
671
 * Caller owns returned value. g_free after usage.
672
 *
673
674
675
676
 * Free-function: g_free
 *
 * Returns: (transfer full) (array length=nbytes): oven-fresh hot data, or
 *     #NULL if @nbytes bytes are not available
677
 */
678
gpointer
679
gst_adapter_take (GstAdapter * adapter, gsize nbytes)
680
{
681
  gpointer data;
682
683
684
685

  g_return_val_if_fail (GST_IS_ADAPTER (adapter), NULL);
  g_return_val_if_fail (nbytes > 0, NULL);

686
687
688
689
  /* we don't have enough data, return NULL. This is unlikely
   * as one usually does an _available() first instead of peeking a
   * random size. */
  if (G_UNLIKELY (nbytes > adapter->size))
690
691
    return NULL;

Wim Taymans's avatar
Wim Taymans committed
692
  data = gst_adapter_take_internal (adapter, nbytes);
693

694
  gst_adapter_flush_unchecked (adapter, nbytes);
695
696
697
698

  return data;
}

699
700
701
702
703
704
705
/**
 * gst_adapter_take_buffer:
 * @adapter: a #GstAdapter
 * @nbytes: the number of bytes to take
 *
 * Returns a #GstBuffer containing the first @nbytes bytes of the
 * @adapter. The returned bytes will be flushed from the adapter.
Wim Taymans's avatar
Wim Taymans committed
706
 * This function is potentially more performant than gst_adapter_take()
707
 * since it can reuse the memory in pushed buffers by subbuffering
708
709
710
711
 * or merging.
 *
 * Caller owns returned value. gst_buffer_unref() after usage.
 *
712
 * Free-function: gst_buffer_unref
713
 *
714
715
716
717
 * Returns: (transfer full): a #GstBuffer containing the first @nbytes of
 *     the adapter, or #NULL if @nbytes bytes are not available
 *
 * Since: 0.10.6
718
719
 */
GstBuffer *
720
gst_adapter_take_buffer (GstAdapter * adapter, gsize nbytes)
721
722
{
  GstBuffer *buffer;
723
  GstBuffer *cur;
724
  gsize hsize, skip;
Wim Taymans's avatar
Wim Taymans committed
725
  guint8 *data;
726
727
728
729

  g_return_val_if_fail (GST_IS_ADAPTER (adapter), NULL);
  g_return_val_if_fail (nbytes > 0, NULL);

Josep Torra's avatar
Josep Torra committed
730
731
  GST_LOG_OBJECT (adapter, "taking buffer of %" G_GSIZE_FORMAT " bytes",
      nbytes);
732

733
  /* we don't have enough data, return NULL. This is unlikely
734
   * as one usually does an _available() first instead of grabbing a
735
736
737
738
739
   * random size. */
  if (G_UNLIKELY (nbytes > adapter->size))
    return NULL;

  cur = adapter->buflist->data;
Wim Taymans's avatar
Wim Taymans committed
740
  skip = adapter->skip;
741
  hsize = gst_buffer_get_size (cur);
742
743

  /* our head buffer has enough data left, return it */
Wim Taymans's avatar
Wim Taymans committed
744
  if (skip == 0 && hsize == nbytes) {
Josep Torra's avatar
Josep Torra committed
745
746
    GST_LOG_OBJECT (adapter, "providing buffer of %" G_GSIZE_FORMAT " bytes"
        " as head buffer", nbytes);
747
748
    buffer = gst_buffer_ref (cur);
    goto done;
Wim Taymans's avatar
Wim Taymans committed
749
  } else if (hsize >= nbytes + skip) {
Josep Torra's avatar
Josep Torra committed
750
751
    GST_LOG_OBJECT (adapter, "providing buffer of %" G_GSIZE_FORMAT " bytes"
        " via region copy", nbytes);
Wim Taymans's avatar
Wim Taymans committed
752
    buffer = gst_buffer_copy_region (cur, GST_BUFFER_COPY_ALL, skip, nbytes);
753
    goto done;
754
755
  }

756
757
758
  if (gst_adapter_try_to_merge_up (adapter, nbytes)) {
    /* Merged something, let's try again for sub-buffering */
    cur = adapter->buflist->data;
759
    if (gst_buffer_get_size (cur) >= nbytes + skip) {
Josep Torra's avatar
Josep Torra committed
760
761
      GST_LOG_OBJECT (adapter, "providing buffer of %" G_GSIZE_FORMAT " bytes"
          " via sub-buffer", nbytes);
Wim Taymans's avatar
Wim Taymans committed
762
      buffer = gst_buffer_copy_region (cur, GST_BUFFER_COPY_ALL, skip, nbytes);
763
      goto done;
764
765
766
    }
  }

Wim Taymans's avatar
Wim Taymans committed
767
768
769
  data = gst_adapter_take_internal (adapter, nbytes);

  buffer = gst_buffer_new ();
770
  gst_buffer_take_memory (buffer, -1,
771
      gst_memory_new_wrapped (0, data, nbytes, 0, nbytes, data, g_free));
772

773
done:
774
  gst_adapter_flush_unchecked (adapter, nbytes);
775
776
777
778

  return buffer;
}

779
780
781
782
783
/**
 * gst_adapter_take_list:
 * @adapter: a #GstAdapter
 * @nbytes: the number of bytes to take
 *
784
 * Returns a #GList of buffers containing the first @nbytes bytes of the
785
786
 * @adapter. The returned bytes will be flushed from the adapter.
 * When the caller can deal with individual buffers, this function is more
787
 * performant because no memory should be copied.
788
789
 *
 * Caller owns returned list and contained buffers. gst_buffer_unref() each
790
 * buffer in the list before freeing the list after usage.
791
 *
792
793
794
 * Returns: (element-type Gst.Buffer) (transfer full): a #GList of buffers
 *     containing the first @nbytes of the adapter, or #NULL if @nbytes bytes
 *     are not available
795
796
 *
 * Since: 0.10.31
797
798
 */
GList *
799
gst_adapter_take_list (GstAdapter * adapter, gsize nbytes)
800
{
801
  GQueue queue = G_QUEUE_INIT;
802
  GstBuffer *cur;
803
  gsize hsize, skip;
804
805
806
807

  g_return_val_if_fail (GST_IS_ADAPTER (adapter), NULL);
  g_return_val_if_fail (nbytes <= adapter->size, NULL);

Josep Torra's avatar
Josep Torra committed
808
  GST_LOG_OBJECT (adapter, "taking %" G_GSIZE_FORMAT " bytes", nbytes);
809
810
811
812

  while (nbytes > 0) {
    cur = adapter->buflist->data;
    skip = adapter->skip;
813
    hsize = MIN (nbytes, gst_buffer_get_size (cur) - skip);
814
815
816

    cur = gst_adapter_take_buffer (adapter, hsize);

817
818
    g_queue_push_tail (&queue, cur);

819
820
    nbytes -= hsize;
  }
821
  return queue.head;
822
823
}

824
825
826
827
/**
 * gst_adapter_available:
 * @adapter: a #GstAdapter
 *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
828
 * Gets the maximum amount of bytes available, that is it returns the maximum
829
 * value that can be supplied to gst_adapter_map() without that function
830
831
 * returning NULL.
 *
832
 * Returns: number of bytes available in @adapter
833
 */
834
gsize
835
836
837
838
839
840
841
842
843
844
845
gst_adapter_available (GstAdapter * adapter)
{
  g_return_val_if_fail (GST_IS_ADAPTER (adapter), 0);

  return adapter->size;
}

/**
 * gst_adapter_available_fast:
 * @adapter: a #GstAdapter
 *
846
 * Gets the maximum number of bytes that are immediately available without
Wim Taymans's avatar
Wim Taymans committed
847
 * requiring any expensive operations (like copying the data into a
848
 * temporary buffer).
849
 *
Wim Taymans's avatar
Wim Taymans committed
850
 * Returns: number of bytes that are available in @adapter without expensive
851
 * operations
852
 */
853
gsize
854
855
gst_adapter_available_fast (GstAdapter * adapter)
{
856
  GstBuffer *cur;
857
  gsize size;
858
  GSList *g;
859

860
861
  g_return_val_if_fail (GST_IS_ADAPTER (adapter), 0);

862
863
  /* no data */
  if (adapter->size == 0)
864
    return 0;
865
866

  /* some stuff we already assembled */
867
868
  if (adapter->assembled_len)
    return adapter->assembled_len;
869

870
871
872
873
  /* take the first non-zero buffer */
  g = adapter->buflist;
  while (TRUE) {
    cur = g->data;
874
    size = gst_buffer_get_size (cur);
875
876
877
878
    if (size != 0)
      break;
    g = g_slist_next (g);
  }
879

Wim Taymans's avatar
Wim Taymans committed
880
  /* we can quickly get the (remaining) data of the first buffer */
881
  return size - adapter->skip;
882
}
883
884

/**
Wim Taymans's avatar
Wim Taymans committed
885
 * gst_adapter_prev_pts:
886
 * @adapter: a #GstAdapter
887
 * @distance: (out) (allow-none): pointer to location for distance, or NULL
888
 *
Wim Taymans's avatar
Wim Taymans committed
889
890
 * Get the pts that was before the current byte in the adapter. When
 * @distance is given, the amount of bytes between the pts and the current
891
892
 * position is returned.
 *
Wim Taymans's avatar
Wim Taymans committed
893
 * The pts is reset to GST_CLOCK_TIME_NONE and the distance is set to 0 when
Wim Taymans's avatar
Wim Taymans committed
894
 * the adapter is first created or when it is cleared. This also means that before
Wim Taymans's avatar
Wim Taymans committed
895
 * the first byte with a pts is removed from the adapter, the pts
Wim Taymans's avatar
Wim Taymans committed
896
 * and distance returned are GST_CLOCK_TIME_NONE and 0 respectively.
897
 *
Wim Taymans's avatar
Wim Taymans committed
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
 * Returns: The previously seen pts.
 */
GstClockTime
gst_adapter_prev_pts (GstAdapter * adapter, guint64 * distance)
{
  g_return_val_if_fail (GST_IS_ADAPTER (adapter), GST_CLOCK_TIME_NONE);

  if (distance)
    *distance = adapter->priv->pts_distance;

  return adapter->priv->pts;
}

/**
 * gst_adapter_prev_dts:
 * @adapter: a #GstAdapter
 * @distance: (out) (allow-none): pointer to location for distance, or NULL
 *
 * Get the dts that was before the current byte in the adapter. When
 * @distance is given, the amount of bytes between the dts and the current
 * position is returned.
 *
 * The dts is reset to GST_CLOCK_TIME_NONE and the distance is set to 0 when
 * the adapter is first created or when it is cleared. This also means that before
 * the first byte with a dts is removed from the adapter, the dts
 * and distance returned are GST_CLOCK_TIME_NONE and 0 respectively.
924
 *
Wim Taymans's avatar
Wim Taymans committed
925
 * Returns: The previously seen dts.
926
927
 */
GstClockTime
Wim Taymans's avatar
Wim Taymans committed
928
gst_adapter_prev_dts (GstAdapter * adapter, guint64 * distance)
929
930
931
932
{
  g_return_val_if_fail (GST_IS_ADAPTER (adapter), GST_CLOCK_TIME_NONE);

  if (distance)
Wim Taymans's avatar
Wim Taymans committed
933
    *distance = adapter->priv->dts_distance;
934

Wim Taymans's avatar
Wim Taymans committed
935
  return adapter->priv->dts;
936
}
Wim Taymans's avatar
Wim Taymans committed
937
938

/**
939
 * gst_adapter_masked_scan_uint32_peek:
Wim Taymans's avatar
Wim Taymans committed
940
941
942
943
944
945
 * @adapter: a #GstAdapter
 * @mask: mask to apply to data before matching against @pattern
 * @pattern: pattern to match (after mask is applied)
 * @offset: offset into the adapter data from which to start scanning, returns
 *          the last scanned position.
 * @size: number of bytes to scan from offset
946
 * @value: pointer to uint32 to return matching data
Wim Taymans's avatar
Wim Taymans committed
947
948
 *
 * Scan for pattern @pattern with applied mask @mask in the adapter data,
949
950
 * starting from offset @offset.  If a match is found, the value that matched
 * is returned through @value, otherwise @value is left untouched.
951
952
953
954
 *
 * The bytes in @pattern and @mask are interpreted left-to-right, regardless
 * of endianness.  All four bytes of the pattern must be present in the
 * adapter for it to match, even if the first or last bytes are masked out.
Wim Taymans's avatar
Wim Taymans committed
955
956
957
958
959
960
 *
 * It is an error to call this function without making sure that there is
 * enough data (offset+size bytes) in the adapter.
 *
 * Returns: offset of the first match, or -1 if no match was found.
 *
961
 * Since: 0.10.30
Wim Taymans's avatar
Wim Taymans committed
962
 */
963
gsize
964
gst_adapter_masked_scan_uint32_peek (GstAdapter * adapter, guint32 mask,
965
    guint32 pattern, gsize offset, gsize size, guint32 * value)
Wim Taymans's avatar
Wim Taymans committed
966
967
{
  GSList *g;
Wim Taymans's avatar
Wim Taymans committed
968
  gsize skip, bsize, i;
Wim Taymans's avatar
Wim Taymans committed
969
  guint32 state;