bytestream.c 17.6 KB
Newer Older
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
/* GStreamer
 * Copyright (C) 2001 Erik Walthinsen <omega@temple-baptist.com>
 *
 * gstbytestream.c: adds a convenient bytestream based API to a pad.
 *
 * 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.
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <gst/gstinfo.h>
27
#include "bytestream.h"
28

29
/* #define BS_DEBUG */
30

31
32
33
34
35
36
37
38
39
40
41
42
#ifdef G_HAVE_ISO_VARARGS

#ifdef BS_DEBUG
# define bs_print(...)	GST_DEBUG (GST_CAT_BUFFER, __VA_ARGS__)
# define bs_status(bs)			gst_bytestream_print_status(bs)
#else
# define bs_print(...)
# define bs_status(bs)
#endif

#elif defined(G_HAVE_GNUC_VARARGS)

43
44
45
46
47
48
49
50
#ifdef BS_DEBUG
# define bs_print(format,args...)	GST_DEBUG (GST_CAT_BUFFER,  format, ## args)
# define bs_status(bs)			gst_bytestream_print_status(bs)
#else
# define bs_print(format,args...)
# define bs_status(bs)
#endif

51
52
#endif

Wim Taymans's avatar
Wim Taymans committed
53
static guint8 *gst_bytestream_assemble (GstByteStream * bs, guint32 len);
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73

/**
 * gst_bytestream_new:
 * @pad: the pad to attach the bytestream to
 *
 * creates a bytestream from the given pad
 *
 * Returns: a new #GstByteStream object
 */
GstByteStream *
gst_bytestream_new (GstPad * pad)
{
  GstByteStream *bs = g_new (GstByteStream, 1);

  bs->pad = pad;
  bs->event = NULL;
  bs->buflist = NULL;
  bs->headbufavail = 0;
  bs->listavail = 0;
  bs->assembled = NULL;
74
  bs->offset = 0LL;
75
  bs->in_seek = FALSE;
76
77
78
79
80
81
82
83
84
85

  return bs;
}

void
gst_bytestream_destroy (GstByteStream * bs)
{
  GSList *walk;

  if (bs->event)
86
    gst_event_unref (bs->event);
87
88
89
90
91
92
93
94
95
96
97
98

  walk = bs->buflist;
  while (walk) {
    gst_buffer_unref (GST_BUFFER (walk->data));
    walk = g_slist_next (walk);
  }
  g_slist_free (bs->buflist);
  if (bs->assembled)
    g_free (bs->assembled);
  g_free (bs);
}

Wim Taymans's avatar
Wim Taymans committed
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
/* HOW THIS WORKS:
 *
 * The fundamental structure is a singly-linked list of buffers.  The
 * buffer on the front is the oldest, and thus the first to read data
 * from.  The number of bytes left to be read in this buffer is stored
 * in bs->headbufavail.  The number of bytes available in the entire
 * list (including the head buffer) is in bs->listavail.
 *
 * When a request is made for data (peek), _fill_bytes is called with
 * the number of bytes needed, but only if the listavail indicates
 * that there aren't already enough.  This calls _get_next_buf until
 * the listavail is sufficient to satisfy the demand.
 *
 * _get_next_buf pulls a buffer from the pad the bytestream is attached
 * to, and shoves it in the list.  There are actually two things it can
 * do.  If there's already a buffer in the list, and the _is_span_fast()
 * test returns true, it will merge it with that last buffer.  Otherwise
 * it will simply tack it onto the end of the list.
 *
 * The _peek itself first checks the simple case of the request fitting
 * within the head buffer, and if so creates a subbuffer and returns.
 * Otherwise, it creates a new buffer and allocates space for the request
 * and calls _assemble to fill it.  We know we have to copy because this
 * case only happens when the _merge wasn't feasible during _get_next_buf.
 *
 * The _flush method repeatedly inspects the head buffer and flushes as
 * much data from it as it needs to, up to the size of the buffer.  If
 * the flush decimates the buffer, it's stripped, unref'd, and removed.
 */


/* get the next buffer
 * if the buffer can be merged with the head buffer, do so
 * else add it onto the head of the 
 */
134
static gboolean
Wim Taymans's avatar
Wim Taymans committed
135
gst_bytestream_get_next_buf (GstByteStream *bs)
136
{
137
  GstBuffer *nextbuf, *lastbuf, *headbuf;
138
  GSList *end;
139
  GstClockTime ts;
140

141
142
143
  /* if there is an event pending, return FALSE */
  if (bs->event)
    return FALSE;
144

145
  bs_print ("get_next_buf: pulling buffer");
146
147
  nextbuf = gst_pad_pull (bs->pad);

148
149
150
  if (!nextbuf)
    return FALSE;

151
152
153
154
  if (GST_IS_EVENT (nextbuf)) {
    bs->event = GST_EVENT (nextbuf);
    return FALSE;
  }
155

156
157
158
  ts = GST_BUFFER_TIMESTAMP (nextbuf);
  if (ts != GST_CLOCK_TIME_NONE)
    bs->last_ts = ts;
159

160
  bs_print ("get_next_buf: got buffer of %d bytes", GST_BUFFER_SIZE (nextbuf));
161

Wim Taymans's avatar
Wim Taymans committed
162
  /* first see if there are any buffers in the list at all */
163
  if (bs->buflist) {
164
    bs_print ("gst_next_buf: there is at least one buffer in the list");
Wim Taymans's avatar
Wim Taymans committed
165
    /* now find the end of the list */
166
    end = g_slist_last (bs->buflist);
Wim Taymans's avatar
Wim Taymans committed
167
    /* get the buffer that's there */
168
169
    lastbuf = GST_BUFFER (end->data);

Wim Taymans's avatar
Wim Taymans committed
170
    /* see if we can marge cheaply */
171
    if (gst_buffer_is_span_fast (lastbuf, nextbuf)) {
172
      bs_print ("get_next_buf: merging new buffer with last buf on list");
Wim Taymans's avatar
Wim Taymans committed
173
      /* it is, let's merge them (this is really an append, but...) */
174
      end->data = gst_buffer_merge (lastbuf, nextbuf);
Wim Taymans's avatar
Wim Taymans committed
175
      /* add to the length of the list */
176
177
      bs->listavail += GST_BUFFER_SIZE (nextbuf);

Wim Taymans's avatar
Wim Taymans committed
178
      /* have to check to see if we merged with the head buffer */
179
180
181
182
183
184
185
      if (end == bs->buflist) {
	bs->headbufavail += GST_BUFFER_SIZE (nextbuf);
      }

      gst_buffer_unref (lastbuf);
      gst_buffer_unref (nextbuf);

Wim Taymans's avatar
Wim Taymans committed
186
      /* if we can't, we just append this buffer */
187
188
    }
    else {
189
      bs_print ("get_next_buf: adding new buffer to the end of the list");
190
      end = g_slist_append (end, nextbuf);
Wim Taymans's avatar
Wim Taymans committed
191
      /* also need to increment length of list and buffer count */
192
193
194
      bs->listavail += GST_BUFFER_SIZE (nextbuf);
    }

Wim Taymans's avatar
Wim Taymans committed
195
    /* if there are no buffers in the list */
196
197
  }
  else {
198
    bs_print ("get_next_buf: buflist is empty, adding new buffer to list");
Wim Taymans's avatar
Wim Taymans committed
199
    /* put this on the end of the list */
200
    bs->buflist = g_slist_append (bs->buflist, nextbuf);
Wim Taymans's avatar
Wim Taymans committed
201
    /* and increment the number of bytes in the list */
202
    bs->listavail = GST_BUFFER_SIZE (nextbuf);
Wim Taymans's avatar
Wim Taymans committed
203
    /* set the head buffer avail to the size */
204
205
206
    bs->headbufavail = GST_BUFFER_SIZE (nextbuf);
  }

207
208
209
210
211
212
  /* a zero offset is a indication that we might need to set the timestamp */ 
  if (bs->offset == 0LL){
    headbuf = GST_BUFFER (bs->buflist->data);
    bs->offset = GST_BUFFER_OFFSET(headbuf);
  }
  
213
214
215
216
  return TRUE;
}

static gboolean
Wim Taymans's avatar
Wim Taymans committed
217
gst_bytestream_fill_bytes (GstByteStream *bs, guint32 len)
218
{
Wim Taymans's avatar
Wim Taymans committed
219
  /* as long as we don't have enough, we get more buffers */
220
  while (bs->listavail < len) {
221
    bs_print ("fill_bytes: there are %d bytes in the list, we need %d", bs->listavail, len);
222
223
224
225
226
227
228
    if (!gst_bytestream_get_next_buf (bs))
      return FALSE;
  }

  return TRUE;
}

229
guint32
Wim Taymans's avatar
Wim Taymans committed
230
gst_bytestream_peek (GstByteStream *bs, GstBuffer **buf, guint32 len)
231
232
233
{
  GstBuffer *headbuf, *retbuf = NULL;

234
235
  g_return_val_if_fail (bs != NULL, 0);
  g_return_val_if_fail (len > 0, 0);
236

237
  bs_print ("peek: asking for %d bytes", len);
238

Wim Taymans's avatar
Wim Taymans committed
239
  /* make sure we have enough */
240
  bs_print ("peek: there are %d bytes in the list", bs->listavail);
241
  if (len > bs->listavail) {
242
243
244
245
246
247
248
249
250
251
252
253
    if (!gst_bytestream_fill_bytes (bs, len)){
      /* we must have an event coming up */
      if (bs->listavail > 0){
        /* we have some data left, len will be shrunk to the amount of data available */
        len = bs->listavail;
      }
      else {
        /* there is no data */
        *buf = retbuf;
        return 0;
      }
    }
254
    bs_print ("peek: there are now %d bytes in the list", bs->listavail);
255
256
257
  }
  bs_status (bs);

Wim Taymans's avatar
Wim Taymans committed
258
  /* extract the head buffer */
259
260
  headbuf = GST_BUFFER (bs->buflist->data);

Wim Taymans's avatar
Wim Taymans committed
261
  /* if the requested bytes are in the current buffer */
262
  bs_print ("peek: headbufavail is %d", bs->headbufavail);
263
  if (len <= bs->headbufavail) {
264
    bs_print ("peek: there are enough bytes in headbuf (need %d, have %d)", len, bs->headbufavail);
Wim Taymans's avatar
Wim Taymans committed
265
    /* create a sub-buffer of the headbuf */
266
267
    retbuf = gst_buffer_create_sub (headbuf, GST_BUFFER_SIZE (headbuf) - bs->headbufavail, len);

Wim Taymans's avatar
Wim Taymans committed
268
    /* otherwise we need to figure out how to assemble one */
269
270
  }
  else {
271
    bs_print ("peek: current buffer is not big enough for len %d", len);
272
273
274
275

    retbuf = gst_buffer_new ();
    GST_BUFFER_SIZE (retbuf) = len;
    GST_BUFFER_DATA (retbuf) = gst_bytestream_assemble (bs, len);
276
    GST_BUFFER_TIMESTAMP (retbuf) = bs->last_ts;
277
278
279
280
    if (GST_BUFFER_OFFSET (headbuf) != -1)
      GST_BUFFER_OFFSET (retbuf) = GST_BUFFER_OFFSET (headbuf) + (GST_BUFFER_SIZE (headbuf) - bs->headbufavail);
  }

281
282
  *buf = retbuf;
  return len;
283
284
}

285
guint32
Wim Taymans's avatar
Wim Taymans committed
286
gst_bytestream_peek_bytes (GstByteStream *bs, guint8** data, guint32 len)
287
288
289
{
  GstBuffer *headbuf;

290
291
  g_return_val_if_fail (bs != NULL, 0);
  g_return_val_if_fail (len > 0, 0);
292

293
  bs_print ("peek_bytes: asking for %d bytes", len);
294
295
296
297
298
  if (bs->assembled) {
    g_free (bs->assembled);
    bs->assembled = NULL;
  }

Wim Taymans's avatar
Wim Taymans committed
299
  /* make sure we have enough */
300
  bs_print ("peek_bytes: there are %d bytes in the list", bs->listavail);
301
  if (len > bs->listavail) {
302
303
304
305
306
307
308
309
310
311
312
313
    if (!gst_bytestream_fill_bytes (bs, len)){
      /* we must have an event coming up */
      if (bs->listavail > 0){
        /* we have some data left, len will be shrunk to the amount of data available */
        len = bs->listavail;
      }
      else {
        /* there is no data */
        *data = NULL;
        return 0;
      }
    }
314
    bs_print ("peek_bytes: there are now %d bytes in the list", bs->listavail);
315
316
317
  }
  bs_status (bs);

Wim Taymans's avatar
Wim Taymans committed
318
  /* extract the head buffer */
319
320
  headbuf = GST_BUFFER (bs->buflist->data);

Wim Taymans's avatar
Wim Taymans committed
321
  /* if the requested bytes are in the current buffer */
322
  bs_print ("peek_bytes: headbufavail is %d", bs->headbufavail);
323
  if (len <= bs->headbufavail) {
324
    bs_print ("peek_bytes: there are enough bytes in headbuf (need %d, have %d)", len, bs->headbufavail);
Wim Taymans's avatar
Wim Taymans committed
325
    /* create a sub-buffer of the headbuf */
326
    *data = GST_BUFFER_DATA (headbuf) + (GST_BUFFER_SIZE (headbuf) - bs->headbufavail);
327

Wim Taymans's avatar
Wim Taymans committed
328
    /* otherwise we need to figure out how to assemble one */
329
330
  }
  else {
331
    bs_print ("peek_bytes: current buffer is not big enough for len %d", len);
332

333
334
    *data = gst_bytestream_assemble (bs, len);
    bs->assembled = *data;
335
336
337
    bs->assembled_len = len;
  }

338
  return len;
339
340
}

Wim Taymans's avatar
Wim Taymans committed
341
342
static guint8*
gst_bytestream_assemble (GstByteStream *bs, guint32 len)
343
344
345
346
347
348
{
  guint8 *data = g_malloc (len);
  GSList *walk;
  guint32 copied = 0;
  GstBuffer *buf;

Wim Taymans's avatar
Wim Taymans committed
349
  /* copy the data from the curbuf */
350
  buf = GST_BUFFER (bs->buflist->data);
351
  bs_print ("assemble: copying %d bytes from curbuf at %d to *data", bs->headbufavail,
352
353
354
355
	    GST_BUFFER_SIZE (buf) - bs->headbufavail);
  memcpy (data, GST_BUFFER_DATA (buf) + GST_BUFFER_SIZE (buf) - bs->headbufavail, bs->headbufavail);
  copied += bs->headbufavail;

Wim Taymans's avatar
Wim Taymans committed
356
  /* asumption is made that the buffers all exist in the list */
357
358
359
360
  walk = g_slist_next (bs->buflist);
  while (copied < len) {
    buf = GST_BUFFER (walk->data);
    if (GST_BUFFER_SIZE (buf) < (len - copied)) {
361
      bs_print ("assemble: copying %d bytes from buf to output offset %d", GST_BUFFER_SIZE (buf), copied);
362
363
364
365
      memcpy (data + copied, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
      copied += GST_BUFFER_SIZE (buf);
    }
    else {
366
      bs_print ("assemble: copying %d bytes from buf to output offset %d", len - copied, copied);
367
368
369
370
371
372
373
374
375
376
      memcpy (data + copied, GST_BUFFER_DATA (buf), len - copied);
      copied = len;
    }
    walk = g_slist_next (walk);
  }

  return data;
}

gboolean
Wim Taymans's avatar
Wim Taymans committed
377
gst_bytestream_flush (GstByteStream *bs, guint32 len)
378
{
379
  bs_print ("flush: flushing %d bytes", len);
380

Wim Taymans's avatar
Wim Taymans committed
381
382
383
  if (len == 0)
    return TRUE;

Wim Taymans's avatar
Wim Taymans committed
384
  /* make sure we have enough */
385
  bs_print ("flush: there are %d bytes in the list", bs->listavail);
386
387
388
  if (len > bs->listavail) {
    if (!gst_bytestream_fill_bytes (bs, len))
      return FALSE;
389
    bs_print ("flush: there are now %d bytes in the list", bs->listavail);
390
391
392
393
394
395
396
397
  }

  gst_bytestream_flush_fast (bs, len);

  return TRUE;
}

void
Wim Taymans's avatar
Wim Taymans committed
398
gst_bytestream_flush_fast (GstByteStream *bs, guint32 len)
399
400
401
{
  GstBuffer *headbuf;

Wim Taymans's avatar
Wim Taymans committed
402
403
404
  if (len == 0)
    return;
		  
405
406
407
408
409
410
411
  g_assert (len <= bs->listavail);

  if (bs->assembled) {
    g_free (bs->assembled);
    bs->assembled = NULL;
  }

412
413
414
  /* update the byte offset */
  bs->offset += len;

Wim Taymans's avatar
Wim Taymans committed
415
  /* repeat until we've flushed enough data */
416
417
418
  while (len > 0) {
    headbuf = GST_BUFFER (bs->buflist->data);

419
    bs_print ("flush: analyzing buffer that's %d bytes long, offset %llu", GST_BUFFER_SIZE (headbuf),
420
421
	      GST_BUFFER_OFFSET (headbuf));

Wim Taymans's avatar
Wim Taymans committed
422
    /* if there's enough to complete the flush */
423
    if (bs->headbufavail > len) {
Wim Taymans's avatar
Wim Taymans committed
424
      /* just trim it off */
425
      bs_print ("flush: trimming %d bytes off end of headbuf", len);
426
427
428
429
      bs->headbufavail -= len;
      bs->listavail -= len;
      len = 0;

Wim Taymans's avatar
Wim Taymans committed
430
      /* otherwise we have to trim the whole buffer */
431
432
    }
    else {
433
      bs_print ("flush: removing head buffer completely");
Wim Taymans's avatar
Wim Taymans committed
434
      /* remove it from the list */
435
      bs->buflist = g_slist_delete_link (bs->buflist, bs->buflist);
Wim Taymans's avatar
Wim Taymans committed
436
      /* trim it from the avail size */
437
      bs->listavail -= bs->headbufavail;
Wim Taymans's avatar
Wim Taymans committed
438
      /* record that we've trimmed this many bytes */
439
      len -= bs->headbufavail;
Wim Taymans's avatar
Wim Taymans committed
440
      /* unref it */
441
442
      gst_buffer_unref (headbuf);

Wim Taymans's avatar
Wim Taymans committed
443
      /* record the new headbufavail */
444
445
      if (bs->buflist) {
	bs->headbufavail = GST_BUFFER_SIZE (GST_BUFFER (bs->buflist->data));
446
	bs_print ("flush: next headbuf is %d bytes", bs->headbufavail);
447
448
      }
      else {
449
	bs_print ("flush: no more bytes at all");
450
451
452
      }
    }

453
    bs_print ("flush: bottom of while(), len is now %d", len);
454
455
456
  }
}

Wim Taymans's avatar
Wim Taymans committed
457
gboolean
458
gst_bytestream_seek (GstByteStream *bs, gint64 offset, GstSeekType method)
Wim Taymans's avatar
Wim Taymans committed
459
{
460
461
462
463
464
465
466
467
  GstRealPad *peer;
  
  g_return_val_if_fail (bs != NULL, FALSE);
  
  peer = GST_RPAD_PEER (bs->pad);

  bs_print ("bs: send event\n");
  if (gst_pad_send_event (GST_PAD (peer), gst_event_new_seek (
Wim Taymans's avatar
Wim Taymans committed
468
469
470
471
			  GST_FORMAT_BYTES | 
			  (method & GST_SEEK_METHOD_MASK) | 
			  GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, 
			  offset))) 
472
  {
Wim Taymans's avatar
Wim Taymans committed
473
    gst_bytestream_flush_fast (bs, bs->listavail);
474

475
476
477
    /* we set the seek flag here. We cannot pull the pad here
     * bacause a seek might occur outisde of the pads cothread context */
    bs->in_seek = TRUE;
478
    
Wim Taymans's avatar
Wim Taymans committed
479
480
    return TRUE;
  }
481
  bs_print ("bs: send event failed\n");
Wim Taymans's avatar
Wim Taymans committed
482
483
484
485
486
487
  return FALSE;
}

guint64
gst_bytestream_tell (GstByteStream *bs)
{
488
489
490
  GstFormat format;
  gint64 value;
  
491
  g_return_val_if_fail (bs != NULL, -1);
492
493
494

  format = GST_FORMAT_BYTES;

495
  if (gst_pad_query (GST_PAD_PEER (bs->pad), GST_QUERY_POSITION, &format, &value)) 
496
497
498
    return value;
  
  return -1;
Wim Taymans's avatar
Wim Taymans committed
499
500
}

501
502
503
504
505
506
507
508
509
510
guint64
gst_bytestream_length (GstByteStream *bs)
{
  GstFormat format;
  gint64 value;
  
  g_return_val_if_fail (bs != NULL, -1);

  format = GST_FORMAT_BYTES;

511
  if (gst_pad_query (GST_PAD_PEER (bs->pad), GST_QUERY_TOTAL, &format, &value)) 
512
513
514
515
516
    return value;
  
  return -1;
}

517
guint32
Wim Taymans's avatar
Wim Taymans committed
518
gst_bytestream_read (GstByteStream *bs, GstBuffer** buf, guint32 len)
519
{
520
  guint32 len_peeked;
521
522

  g_return_val_if_fail (bs != NULL, -1);
523
524
525
526
  
  len_peeked = gst_bytestream_peek (bs, buf, len);
  if (len_peeked == 0)
    return 0;
527

528
  gst_bytestream_flush_fast (bs, len_peeked);
529

530
  return len_peeked;
531
532
}

533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
/**
 * gst_bytestream_size_hint
 * @bs: a bytestream
 * @size: the size to hint
 *
 * Give a hint that we are going to read chunks of the given size
 *
 * Returns: TRUE if the hint was accepted
 */
gboolean
gst_bytestream_size_hint (GstByteStream *bs, guint32 size)
{
  GstEvent *event;

  g_return_val_if_fail (bs != NULL, FALSE);

  event = gst_event_new_size (GST_FORMAT_BYTES, size);

  return gst_pad_send_event (GST_PAD_PEER (bs->pad), event);
}
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569

/**
 * gst_bytestream_get_status
 * @bs: a bytestream
 * @avail_out: total number of bytes buffered
 * @event_out: an event
 *
 * When an event occurs, the bytestream will return NULL.  You must
 * retrieve the event using this API before reading more bytes from
 * the stream.
 *
 * It is possible for the bytestream to return NULL due to running
 * out of buffers, however, this indicates a bug because an EOS
 * event should have been sent.
 */
void
gst_bytestream_get_status (GstByteStream *bs,
Wim Taymans's avatar
Wim Taymans committed
570
571
			   guint32 	 *avail_out,
			   GstEvent 	**event_out)
572
573
574
575
{
  if (avail_out)
    *avail_out = bs->listavail;

Wim Taymans's avatar
Wim Taymans committed
576
577
578
579
  if (event_out) {
    *event_out = bs->event;
    bs->event = NULL;
  }
580
581
}

582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
/**
 * gst_bytestream_get_timestamp
 * @bs: a bytestream
 *
 * Get the timestamp of the first data in the bytestream.  If no data
 * exists 1 byte is read to load a new buffer.
 *
 * This function will not check input buffer boundries.  It is  possible
 * the next read could span two or more input buffers with different
 * timestamps.
 */
guint64
gst_bytestream_get_timestamp (GstByteStream *bs)
{
  GstBuffer *headbuf;

  g_return_val_if_fail (bs != NULL, 0);

600
  bs_print ("get_timestamp: getting timestamp");
601
602
603

  /* make sure we have a buffer */
  if (bs->listavail == 0) {
604
    bs_print ("gst_timestamp: fetching a buffer");
605
606
607
608
609
610
611
612
613
614
    if (!gst_bytestream_fill_bytes (bs, 1))
      return 0;
  }

  /* extract the head buffer */
  headbuf = GST_BUFFER (bs->buflist->data);

  return GST_BUFFER_TIMESTAMP (headbuf);
}

615
616
617
618
619
620
void
gst_bytestream_print_status (GstByteStream * bs)
{
  GSList *walk;
  GstBuffer *buf;

621
622
  bs_print ("STATUS: head buffer has %d bytes available", bs->headbufavail);
  bs_print ("STATUS: list has %d bytes available", bs->listavail);
623
624
625
626
627
  walk = bs->buflist;
  while (walk) {
    buf = GST_BUFFER (walk->data);
    walk = g_slist_next (walk);

628
    bs_print ("STATUS: buffer starts at %llu and is %d bytes long", GST_BUFFER_OFFSET (buf), GST_BUFFER_SIZE (buf));
629
630
  }
}
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645


static gboolean
plugin_init (GModule *module, GstPlugin *plugin)
{
  gst_plugin_set_longname (plugin, "GstByteStream: a byte-oriented layer on top of buffer-passing");
  return TRUE;
}

GstPluginDesc plugin_desc = {
  GST_VERSION_MAJOR,
  GST_VERSION_MINOR,
  "gstbytestream",
  plugin_init
};