First THREADED backport attempt, focusing on adding locks and making sure the API...
[platform/upstream/gstreamer.git] / libs / gst / bytestream / bytestream.c
1 /* GStreamer
2  * Copyright (C) 2001 Erik Walthinsen <omega@temple-baptist.com>
3  *
4  * gstbytestream.c: adds a convenient bytestream based API to a pad.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #  include "config.h"
24 #endif
25
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29
30 #include <gst/gstinfo.h>
31 #include <gst/gstplugin.h>
32 #include <gst/gstversion.h>
33 #include <gst/gstutils.h>
34 #include "bytestream.h"
35
36 GST_DEBUG_CATEGORY_STATIC (debug_bs);
37 #define GST_CAT_DEFAULT debug_bs
38
39 static guint8 *gst_bytestream_assemble (GstByteStream * bs, guint32 len);
40
41 static inline void
42 gst_bytestream_init (GstByteStream * bs)
43 {
44   bs->event = NULL;
45   bs->buflist = NULL;
46   bs->headbufavail = 0;
47   bs->listavail = 0;
48   bs->assembled = NULL;
49   bs->offset = 0LL;
50   bs->in_seek = FALSE;
51 }
52 static inline void
53 gst_bytestream_exit (GstByteStream * bs)
54 {
55   GSList *walk;
56
57   if (bs->event)
58     gst_event_unref (bs->event);
59
60   walk = bs->buflist;
61   while (walk) {
62     gst_buffer_unref (GST_BUFFER (walk->data));
63     walk = g_slist_next (walk);
64   }
65   g_slist_free (bs->buflist);
66
67   g_free (bs->assembled);
68 }
69
70 /**
71  * gst_bytestream_new:
72  * @pad: the pad to attach the bytestream to
73  *
74  * creates a bytestream from the given pad
75  *
76  * Returns: a new #GstByteStream object.
77  */
78 GstByteStream *
79 gst_bytestream_new (GstPad * pad)
80 {
81   GstByteStream *bs = g_new (GstByteStream, 1);
82
83   bs->pad = pad;
84   gst_bytestream_init (bs);
85
86   return bs;
87 }
88
89 /**
90  * gst_bytestream_destroy:
91  * @bs: the bytestream object to destroy
92  *
93  * destroy the bytestream object and free its resources.
94  */
95 void
96 gst_bytestream_destroy (GstByteStream * bs)
97 {
98   gst_bytestream_exit (bs);
99   g_free (bs);
100 }
101
102 void
103 gst_bytestream_reset (GstByteStream * bs)
104 {
105   /* free all data */
106   gst_bytestream_exit (bs);
107   /* reset data to clean state */
108   gst_bytestream_init (bs);
109 }
110
111 /* HOW THIS WORKS:
112  *
113  * The fundamental structure is a singly-linked list of buffers.  The
114  * buffer on the front is the oldest, and thus the first to read data
115  * from.  The number of bytes left to be read in this buffer is stored
116  * in bs->headbufavail.  The number of bytes available in the entire
117  * list (including the head buffer) is in bs->listavail.
118  *
119  * When a request is made for data (peek), _fill_bytes is called with
120  * the number of bytes needed, but only if the listavail indicates
121  * that there aren't already enough.  This calls _get_next_buf until
122  * the listavail is sufficient to satisfy the demand.
123  *
124  * _get_next_buf pulls a buffer from the pad the bytestream is attached
125  * to, and shoves it in the list.  There are actually two things it can
126  * do.  If there's already a buffer in the list, and the _is_span_fast()
127  * test returns true, it will merge it with that last buffer.  Otherwise
128  * it will simply tack it onto the end of the list.
129  *
130  * The _peek itself first checks the simple case of the request fitting
131  * within the head buffer, and if so creates a subbuffer and returns.
132  * Otherwise, it creates a new buffer and allocates space for the request
133  * and calls _assemble to fill it.  We know we have to copy because this
134  * case only happens when the _merge wasn't feasible during _get_next_buf.
135  *
136  * The _flush method repeatedly inspects the head buffer and flushes as
137  * much data from it as it needs to, up to the size of the buffer.  If
138  * the flush decimates the buffer, it's stripped, unref'd, and removed.
139  */
140
141
142 /* get the next buffer
143  * if the buffer can be merged with the head buffer, do so
144  * else add it onto the head of the 
145  */
146 static gboolean
147 gst_bytestream_get_next_buf (GstByteStream * bs)
148 {
149   GstBuffer *nextbuf, *lastbuf, *headbuf;
150   GSList *end;
151
152   /* if there is an event pending, return FALSE */
153   if (bs->event)
154     return FALSE;
155
156   GST_DEBUG ("get_next_buf: pulling buffer");
157   nextbuf = GST_BUFFER (gst_pad_pull (bs->pad));
158
159   if (!nextbuf)
160     return FALSE;
161
162   if (GST_IS_EVENT (nextbuf)) {
163     bs->event = GST_EVENT (nextbuf);
164     return FALSE;
165   }
166
167   if (GST_BUFFER_TIMESTAMP_IS_VALID (nextbuf))
168     bs->last_ts = GST_BUFFER_TIMESTAMP (nextbuf);
169
170   GST_DEBUG ("get_next_buf: got buffer of %d bytes", GST_BUFFER_SIZE (nextbuf));
171
172   /* first see if there are any buffers in the list at all */
173   if (bs->buflist) {
174     GST_DEBUG ("gst_next_buf: there is at least one buffer in the list");
175     /* now find the end of the list */
176     end = g_slist_last (bs->buflist);
177     /* get the buffer that's there */
178     lastbuf = GST_BUFFER (end->data);
179
180     /* see if we can marge cheaply */
181     if (gst_buffer_is_span_fast (lastbuf, nextbuf)) {
182       GST_DEBUG ("get_next_buf: merging new buffer with last buf on list");
183       /* it is, let's merge them (this is really an append, but...) */
184       end->data = gst_buffer_merge (lastbuf, nextbuf);
185       /* add to the length of the list */
186       bs->listavail += GST_BUFFER_SIZE (nextbuf);
187
188       /* have to check to see if we merged with the head buffer */
189       if (end == bs->buflist) {
190         bs->headbufavail += GST_BUFFER_SIZE (nextbuf);
191       }
192
193       gst_buffer_unref (lastbuf);
194       gst_buffer_unref (nextbuf);
195
196       /* if we can't, we just append this buffer */
197     } else {
198       GST_DEBUG ("get_next_buf: adding new buffer to the end of the list");
199       end = g_slist_append (end, nextbuf);
200       /* also need to increment length of list and buffer count */
201       bs->listavail += GST_BUFFER_SIZE (nextbuf);
202     }
203
204     /* if there are no buffers in the list */
205   } else {
206     GST_DEBUG ("get_next_buf: buflist is empty, adding new buffer to list");
207     /* put this on the end of the list */
208     bs->buflist = g_slist_append (bs->buflist, nextbuf);
209     /* and increment the number of bytes in the list */
210     bs->listavail = GST_BUFFER_SIZE (nextbuf);
211     /* set the head buffer avail to the size */
212     bs->headbufavail = GST_BUFFER_SIZE (nextbuf);
213   }
214
215   /* a zero offset is a indication that we might need to set the timestamp */
216   if (bs->offset == 0LL) {
217     headbuf = GST_BUFFER (bs->buflist->data);
218     bs->offset = GST_BUFFER_OFFSET (headbuf);
219   }
220
221   return TRUE;
222 }
223
224 static gboolean
225 gst_bytestream_fill_bytes (GstByteStream * bs, guint32 len)
226 {
227   /* as long as we don't have enough, we get more buffers */
228   while (bs->listavail < len) {
229     GST_DEBUG ("fill_bytes: there are %d bytes in the list, we need %d",
230         bs->listavail, len);
231     if (!gst_bytestream_get_next_buf (bs))
232       return FALSE;
233   }
234
235   return TRUE;
236 }
237
238 /**
239  * gst_bytestream_peek:
240  * @bs: the bytestream to peek
241  * @buf: pointer to a variable that can hold a buffer pointer.
242  * @len: the number of bytes to peek
243  *
244  * Peeks len bytes into the bytestream, the result is returned as
245  * a #GstBuffer. Unref the buffer after usage.
246  * This function can return less bytes than requested. In that case,
247  * an event might have happened which you can retrieve with
248  * gst_bytestream_get_status().
249  *
250  * Returns: The number of bytes successfully peeked.
251  */
252 guint32
253 gst_bytestream_peek (GstByteStream * bs, GstBuffer ** buf, guint32 len)
254 {
255   GstBuffer *headbuf, *retbuf = NULL;
256
257   g_return_val_if_fail (bs != NULL, 0);
258   g_return_val_if_fail (buf != NULL, 0);
259
260   if (len == 0)
261     return 0;
262
263   GST_DEBUG ("peek: asking for %d bytes", len);
264
265   /* make sure we have enough */
266   GST_DEBUG ("peek: there are %d bytes in the list", bs->listavail);
267   if (len > bs->listavail) {
268     if (!gst_bytestream_fill_bytes (bs, len)) {
269       /* we must have an event coming up */
270       if (bs->listavail > 0) {
271         /* we have some data left, len will be shrunk to the amount of data available */
272         len = bs->listavail;
273       } else {
274         /* there is no data */
275         *buf = retbuf;
276         return 0;
277       }
278     }
279     GST_DEBUG ("peek: there are now %d bytes in the list", bs->listavail);
280   }
281   gst_bytestream_print_status (bs);
282
283   /* extract the head buffer */
284   headbuf = GST_BUFFER (bs->buflist->data);
285
286   /* if the requested bytes are in the current buffer */
287   GST_DEBUG ("peek: headbufavail is %d", bs->headbufavail);
288   if (len <= bs->headbufavail) {
289     GST_DEBUG ("peek: there are enough bytes in headbuf (need %d, have %d)",
290         len, bs->headbufavail);
291     /* create a sub-buffer of the headbuf */
292     retbuf =
293         gst_buffer_create_sub (headbuf,
294         GST_BUFFER_SIZE (headbuf) - bs->headbufavail, len);
295     GST_BUFFER_OFFSET (retbuf) =
296         GST_BUFFER_OFFSET (headbuf) + GST_BUFFER_SIZE (headbuf) -
297         bs->headbufavail;
298
299   }
300   /* otherwise we need to figure out how to assemble one */
301   else {
302     GST_DEBUG ("peek: current buffer is not big enough for len %d", len);
303
304     retbuf = gst_buffer_new ();
305     GST_BUFFER_SIZE (retbuf) = len;
306     GST_BUFFER_DATA (retbuf) = gst_bytestream_assemble (bs, len);
307     GST_BUFFER_TIMESTAMP (retbuf) = bs->last_ts;
308   }
309
310   *buf = retbuf;
311   return len;
312 }
313
314 /**
315  * gst_bytestream_peek_bytes:
316  * @bs: the bytestream to peek
317  * @data: pointer to a variable that can hold a guint8 pointer.
318  * @len: the number of bytes to peek
319  *
320  * Peeks len bytes into the bytestream, the result is returned as
321  * a pointer to a guint8*. The data pointed to be data should not
322  * be freed and will become invalid after performing the next bytestream
323  * operation.
324  * This function can return less bytes than requested. In that case,
325  * an event might have happened which you can retrieve with
326  * gst_bytestream_get_status().
327  *
328  * Returns: The number of bytes successfully peeked.
329  */
330 guint32
331 gst_bytestream_peek_bytes (GstByteStream * bs, guint8 ** data, guint32 len)
332 {
333   GstBuffer *headbuf;
334
335   g_return_val_if_fail (bs != NULL, 0);
336   g_return_val_if_fail (data != NULL, 0);
337   g_return_val_if_fail (len > 0, 0);
338
339   GST_DEBUG ("peek_bytes: asking for %d bytes", len);
340   if (bs->assembled) {
341     if (bs->assembled_len >= len) {
342       *data = bs->assembled;
343       return len;
344     }
345     g_free (bs->assembled);
346     bs->assembled = NULL;
347   }
348
349   /* make sure we have enough */
350   GST_DEBUG ("peek_bytes: there are %d bytes in the list", bs->listavail);
351   if (len > bs->listavail) {
352     if (!gst_bytestream_fill_bytes (bs, len)) {
353       /* we must have an event coming up */
354       if (bs->listavail > 0) {
355         /* we have some data left, len will be shrunk to the amount of data available */
356         len = bs->listavail;
357       } else {
358         /* there is no data */
359         *data = NULL;
360         return 0;
361       }
362     }
363     GST_DEBUG ("peek_bytes: there are now %d bytes in the list", bs->listavail);
364   }
365   gst_bytestream_print_status (bs);
366
367   /* extract the head buffer */
368   headbuf = GST_BUFFER (bs->buflist->data);
369
370   /* if the requested bytes are in the current buffer */
371   GST_DEBUG ("peek_bytes: headbufavail is %d", bs->headbufavail);
372   if (len <= bs->headbufavail) {
373     GST_DEBUG
374         ("peek_bytes: there are enough bytes in headbuf (need %d, have %d)",
375         len, bs->headbufavail);
376     /* create a sub-buffer of the headbuf */
377     *data =
378         GST_BUFFER_DATA (headbuf) + (GST_BUFFER_SIZE (headbuf) -
379         bs->headbufavail);
380
381   }
382   /* otherwise we need to figure out how to assemble one */
383   else {
384     GST_DEBUG ("peek_bytes: current buffer is not big enough for len %d", len);
385
386     *data = gst_bytestream_assemble (bs, len);
387     bs->assembled = *data;
388     bs->assembled_len = len;
389   }
390
391   return len;
392 }
393
394 static guint8 *
395 gst_bytestream_assemble (GstByteStream * bs, guint32 len)
396 {
397   guint8 *data = g_malloc (len);
398   GSList *walk;
399   guint32 copied = 0;
400   GstBuffer *buf;
401
402   /* copy the data from the curbuf */
403   buf = GST_BUFFER (bs->buflist->data);
404   GST_DEBUG ("assemble: copying %d bytes from curbuf at %d to *data",
405       bs->headbufavail, GST_BUFFER_SIZE (buf) - bs->headbufavail);
406   memcpy (data,
407       GST_BUFFER_DATA (buf) + GST_BUFFER_SIZE (buf) - bs->headbufavail,
408       bs->headbufavail);
409   copied += bs->headbufavail;
410
411   /* asumption is made that the buffers all exist in the list */
412   walk = g_slist_next (bs->buflist);
413   while (copied < len) {
414     buf = GST_BUFFER (walk->data);
415     if (GST_BUFFER_SIZE (buf) < (len - copied)) {
416       GST_DEBUG ("assemble: copying %d bytes from buf to output offset %d",
417           GST_BUFFER_SIZE (buf), copied);
418       memcpy (data + copied, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
419       copied += GST_BUFFER_SIZE (buf);
420     } else {
421       GST_DEBUG ("assemble: copying %d bytes from buf to output offset %d",
422           len - copied, copied);
423       memcpy (data + copied, GST_BUFFER_DATA (buf), len - copied);
424       copied = len;
425     }
426     walk = g_slist_next (walk);
427   }
428
429   return data;
430 }
431
432 /**
433  * gst_bytestream_flush:
434  * @bs: the bytestream to flush
435  * @len: the number of bytes to flush
436  *
437  * Flush len bytes from the bytestream.
438  * This function can return FALSE when the number of
439  * bytes could not be flushed due to an event. In that case,
440  * you can get the number of available bytes before the event
441  * with gst_bytestream_get_status().
442  *
443  * Returns: TRUE if the number of bytes could be flushed.
444  */
445 gboolean
446 gst_bytestream_flush (GstByteStream * bs, guint32 len)
447 {
448   GST_DEBUG ("flush: flushing %d bytes", len);
449
450   if (len == 0)
451     return TRUE;
452
453   /* make sure we have enough */
454   GST_DEBUG ("flush: there are %d bytes in the list", bs->listavail);
455   if (len > bs->listavail) {
456     if (!gst_bytestream_fill_bytes (bs, len)) {
457       return FALSE;
458     }
459     GST_DEBUG ("flush: there are now %d bytes in the list", bs->listavail);
460   }
461
462   gst_bytestream_flush_fast (bs, len);
463
464   return TRUE;
465 }
466
467 /**
468  * gst_bytestream_flush_fast:
469  * @bs: the bytestream to flush
470  * @len: the number of bytes to flush
471  *
472  * Flushes len bytes from the bytestream. This function
473  * is faster than gst_bytestream_flush() but only works
474  * when you have recently peeked no less than len bytes
475  * with gst_bytestream_peek() or gst_bytestream_peek_bytes().
476  */
477 void
478 gst_bytestream_flush_fast (GstByteStream * bs, guint32 len)
479 {
480   GstBuffer *headbuf;
481
482   if (len == 0)
483     return;
484
485   g_assert (len <= bs->listavail);
486
487   if (bs->assembled) {
488     g_free (bs->assembled);
489     bs->assembled = NULL;
490   }
491
492   /* update the byte offset */
493   bs->offset += len;
494
495   /* repeat until we've flushed enough data */
496   while (len > 0) {
497     headbuf = GST_BUFFER (bs->buflist->data);
498
499     GST_DEBUG ("flush: analyzing buffer that's %d bytes long, offset %"
500         G_GUINT64_FORMAT, GST_BUFFER_SIZE (headbuf),
501         GST_BUFFER_OFFSET (headbuf));
502
503     /* if there's enough to complete the flush */
504     if (bs->headbufavail > len) {
505       /* just trim it off */
506       GST_DEBUG ("flush: trimming %d bytes off end of headbuf", len);
507       bs->headbufavail -= len;
508       bs->listavail -= len;
509       len = 0;
510
511       /* otherwise we have to trim the whole buffer */
512     } else {
513       GST_DEBUG ("flush: removing head buffer completely");
514       /* remove it from the list */
515       bs->buflist = g_slist_delete_link (bs->buflist, bs->buflist);
516       /* trim it from the avail size */
517       bs->listavail -= bs->headbufavail;
518       /* record that we've trimmed this many bytes */
519       len -= bs->headbufavail;
520       /* unref it */
521       gst_buffer_unref (headbuf);
522
523       /* record the new headbufavail */
524       if (bs->buflist) {
525         bs->headbufavail = GST_BUFFER_SIZE (GST_BUFFER (bs->buflist->data));
526         GST_DEBUG ("flush: next headbuf is %d bytes", bs->headbufavail);
527       } else {
528         GST_DEBUG ("flush: no more bytes at all");
529       }
530     }
531
532     GST_DEBUG ("flush: bottom of while(), len is now %d", len);
533   }
534 }
535
536 /**
537  * gst_bytestream_seek:
538  * @bs: the bytestream to seek
539  * @offset: the byte offset to seek to
540  * @method: the seek method.
541  *
542  * Perform a seek on the bytestream to the given offset.
543  * The method can be one of GST_SEEK_METHOD_CUR, GST_SEEK_METHOD_SET,
544  * GST_SEEK_METHOD_END.
545  * This seek will also flush any pending data in the bytestream or
546  * peer elements.
547  *
548  * Returns: TRUE when the seek succeeded.
549  */
550 gboolean
551 gst_bytestream_seek (GstByteStream * bs, gint64 offset, GstSeekType method)
552 {
553   GstRealPad *peer;
554
555   g_return_val_if_fail (bs != NULL, FALSE);
556
557   peer = GST_RPAD_PEER (bs->pad);
558
559   GST_DEBUG ("bs: send event");
560   if (gst_pad_send_event (GST_PAD (peer), gst_event_new_seek (GST_FORMAT_BYTES |
561               (method & GST_SEEK_METHOD_MASK) |
562               GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, offset))) {
563     gst_bytestream_flush_fast (bs, bs->listavail);
564
565     /* we set the seek flag here. We cannot pull the pad here
566      * bacause a seek might occur outisde of the pads cothread context */
567     bs->in_seek = TRUE;
568
569     return TRUE;
570   }
571   GST_DEBUG ("bs: send event failed\n");
572   return FALSE;
573 }
574
575 /**
576  * gst_bytestream_tell
577  * @bs: a bytestream
578  *
579  * Get the current byteoffset in the bytestream.
580  *
581  * Returns: the offset or -1 on error.
582  */
583 guint64
584 gst_bytestream_tell (GstByteStream * bs)
585 {
586   GstFormat format;
587   gint64 value;
588
589   g_return_val_if_fail (bs != NULL, -1);
590
591   format = GST_FORMAT_BYTES;
592
593   if (gst_pad_query (GST_PAD_PEER (bs->pad), GST_QUERY_POSITION, &format,
594           &value)) {
595     return value - bs->listavail;
596   }
597
598   return -1;
599 }
600
601 /**
602  * gst_bytestream_length
603  * @bs: a bytestream
604  *
605  * Get the total length of the bytestream.
606  *
607  * Returns: the total length or -1 on error.
608  */
609 guint64
610 gst_bytestream_length (GstByteStream * bs)
611 {
612   GstFormat format;
613   gint64 value;
614
615   g_return_val_if_fail (bs != NULL, -1);
616
617   format = GST_FORMAT_BYTES;
618
619   if (gst_pad_query (GST_PAD_PEER (bs->pad), GST_QUERY_TOTAL, &format, &value))
620     return value;
621
622   return -1;
623 }
624
625 /**
626  * gst_bytestream_read:
627  * @bs: the bytestream to read
628  * @buf: pointer to a variable that can hold a buffer pointer.
629  * @len: the number of bytes to read
630  *
631  * Read len bytes from the bytestream, the result is returned as
632  * a #GstBuffer. Unref the buffer after usage.
633  * This function can return less bytes than requested. In that case,
634  * an event might have happened which you can retrieve with
635  * gst_bytestream_get_status().
636  *
637  * Returns: The number of bytes successfully read.
638  */
639 guint32
640 gst_bytestream_read (GstByteStream * bs, GstBuffer ** buf, guint32 len)
641 {
642   guint32 len_peeked;
643
644   g_return_val_if_fail (bs != NULL, -1);
645
646   len_peeked = gst_bytestream_peek (bs, buf, len);
647   if (len_peeked == 0)
648     return 0;
649
650   gst_bytestream_flush_fast (bs, len_peeked);
651
652   return len_peeked;
653 }
654
655 /**
656  * gst_bytestream_size_hint
657  * @bs: a bytestream
658  * @size: the size to hint
659  *
660  * Give a hint that we are going to read chunks of the given size.
661  * Giving size hints to the peer element might improve performance
662  * since less buffers need to be merged.
663  *
664  * Returns: TRUE if the hint was accepted.
665  */
666 gboolean
667 gst_bytestream_size_hint (GstByteStream * bs, guint32 size)
668 {
669   GstEvent *event;
670
671   g_return_val_if_fail (bs != NULL, FALSE);
672
673   event = gst_event_new_size (GST_FORMAT_BYTES, size);
674
675   return gst_pad_send_event (GST_PAD_PEER (bs->pad), event);
676 }
677
678 /**
679  * gst_bytestream_get_status
680  * @bs: a bytestream
681  * @avail_out: total number of bytes buffered
682  * @event_out: an event
683  *
684  * When an event occurs, the bytestream operations return a value less
685  * than the requested length. You must retrieve the event using this API 
686  * before reading more bytes from the stream.
687  */
688 void
689 gst_bytestream_get_status (GstByteStream * bs,
690     guint32 * avail_out, GstEvent ** event_out)
691 {
692   if (avail_out)
693     *avail_out = bs->listavail;
694
695   if (event_out) {
696     *event_out = bs->event;
697     bs->event = NULL;
698   }
699 }
700
701 /**
702  * gst_bytestream_get_timestamp
703  * @bs: a bytestream
704  *
705  * Get the timestamp of the first data in the bytestream.  If no data
706  * exists 1 byte is read to load a new buffer.
707  *
708  * This function will not check input buffer boundries.  It is  possible
709  * the next read could span two or more input buffers with different
710  * timestamps.
711  *
712  * Returns: a timestamp.
713  */
714 guint64
715 gst_bytestream_get_timestamp (GstByteStream * bs)
716 {
717   GstBuffer *headbuf;
718
719   g_return_val_if_fail (bs != NULL, 0);
720
721   GST_DEBUG ("get_timestamp: getting timestamp");
722
723   /* make sure we have a buffer */
724   if (bs->listavail == 0) {
725     GST_DEBUG ("gst_timestamp: fetching a buffer");
726     if (!gst_bytestream_fill_bytes (bs, 1))
727       return 0;
728   }
729
730   /* extract the head buffer */
731   headbuf = GST_BUFFER (bs->buflist->data);
732
733   return GST_BUFFER_TIMESTAMP (headbuf);
734 }
735
736 /**
737  * gst_bytestream_print_status
738  * @bs: a bytestream
739  *
740  * Print the current status of the bytestream object. mainly
741  * used for debugging purposes.
742  */
743 void
744 gst_bytestream_print_status (GstByteStream * bs)
745 {
746   GSList *walk;
747   GstBuffer *buf;
748
749   GST_DEBUG ("STATUS: head buffer has %d bytes available", bs->headbufavail);
750   GST_DEBUG ("STATUS: list has %d bytes available", bs->listavail);
751   walk = bs->buflist;
752   while (walk) {
753     buf = GST_BUFFER (walk->data);
754     walk = g_slist_next (walk);
755
756     GST_DEBUG ("STATUS: buffer starts at %" G_GUINT64_FORMAT
757         " and is %d bytes long", GST_BUFFER_OFFSET (buf),
758         GST_BUFFER_SIZE (buf));
759   }
760 }
761
762 static gboolean
763 plugin_init (GstPlugin * plugin)
764 {
765   GST_DEBUG_CATEGORY_INIT (debug_bs, "bytestream", 0, "bytestream library");
766
767   return TRUE;
768 }
769
770 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
771     GST_VERSION_MINOR,
772     "gstbytestream",
773     "a byte-oriented layer on top of buffer-passing",
774     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN)