• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) <2004> Thomas Vander Stichele <thomas at apestaart dot org>
3  * Copyright (C) 2006 Andy Wingo <wingo@pobox.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 /**
22  * SECTION:element-vorbisparse
23  * @title: vorbisparse
24  * @see_also: vorbisdec, oggdemux, theoraparse
25  *
26  * The vorbisparse element will parse the header packets of the Vorbis
27  * stream and put them as the streamheader in the caps. This is used in the
28  * multifdsink case where you want to stream live vorbis streams to multiple
29  * clients, each client has to receive the streamheaders first before they can
30  * consume the vorbis packets.
31  *
32  * This element also makes sure that the buffers that it pushes out are properly
33  * timestamped and that their offset and offset_end are set. The buffers that
34  * vorbisparse outputs have all of the metadata that oggmux expects to receive,
35  * which allows you to (for example) remux an ogg/vorbis file.
36  *
37  * ## Example pipelines
38  * |[
39  * gst-launch-1.0 -v filesrc location=sine.ogg ! oggdemux ! vorbisparse ! fakesink
40  * ]|
41  *  This pipeline shows that the streamheader is set in the caps, and that each
42  * buffer has the timestamp, duration, offset, and offset_end set.
43  * |[
44  * gst-launch-1.0 filesrc location=sine.ogg ! oggdemux ! vorbisparse \
45  *            ! oggmux ! filesink location=sine-remuxed.ogg
46  * ]|
47  *  This pipeline shows remuxing. sine-remuxed.ogg might not be exactly the same
48  * as sine.ogg, but they should produce exactly the same decoded data.
49  *
50  */
51 
52 #ifdef HAVE_CONFIG_H
53 #  include "config.h"
54 #endif
55 
56 #include "gstvorbiselements.h"
57 #include "gstvorbisparse.h"
58 
59 GST_DEBUG_CATEGORY_STATIC (vorbisparse_debug);
60 #define GST_CAT_DEFAULT vorbisparse_debug
61 
62 static GstStaticPadTemplate vorbis_parse_sink_factory =
63 GST_STATIC_PAD_TEMPLATE ("sink",
64     GST_PAD_SINK,
65     GST_PAD_ALWAYS,
66     GST_STATIC_CAPS ("audio/x-vorbis")
67     );
68 
69 static GstStaticPadTemplate vorbis_parse_src_factory =
70 GST_STATIC_PAD_TEMPLATE ("src",
71     GST_PAD_SRC,
72     GST_PAD_ALWAYS,
73     GST_STATIC_CAPS ("audio/x-vorbis")
74     );
75 
76 #define gst_vorbis_parse_parent_class parent_class
77 G_DEFINE_TYPE (GstVorbisParse, gst_vorbis_parse, GST_TYPE_ELEMENT);
78 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (vorbisparse, "vorbisparse",
79     GST_RANK_NONE, GST_TYPE_VORBIS_PARSE,
80     GST_DEBUG_CATEGORY_INIT (vorbisparse_debug, "vorbisparse", 0,
81         "vorbis parsing element");
82     vorbis_element_init (plugin));
83 
84 static GstFlowReturn vorbis_parse_chain (GstPad * pad, GstObject * parent,
85     GstBuffer * buffer);
86 static GstStateChangeReturn vorbis_parse_change_state (GstElement * element,
87     GstStateChange transition);
88 static gboolean vorbis_parse_sink_event (GstPad * pad, GstObject * parent,
89     GstEvent * event);
90 static gboolean vorbis_parse_src_query (GstPad * pad, GstObject * parent,
91     GstQuery * query);
92 static gboolean vorbis_parse_convert (GstPad * pad, GstFormat src_format,
93     gint64 src_value, GstFormat * dest_format, gint64 * dest_value);
94 static GstFlowReturn vorbis_parse_parse_packet (GstVorbisParse * parse,
95     GstBuffer * buf);
96 
97 static void
gst_vorbis_parse_class_init(GstVorbisParseClass * klass)98 gst_vorbis_parse_class_init (GstVorbisParseClass * klass)
99 {
100   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
101 
102   gstelement_class->change_state = vorbis_parse_change_state;
103 
104   gst_element_class_add_static_pad_template (gstelement_class,
105       &vorbis_parse_src_factory);
106   gst_element_class_add_static_pad_template (gstelement_class,
107       &vorbis_parse_sink_factory);
108   gst_element_class_set_static_metadata (gstelement_class, "VorbisParse",
109       "Codec/Parser/Audio", "parse raw vorbis streams",
110       "Thomas Vander Stichele <thomas at apestaart dot org>");
111 
112   klass->parse_packet = GST_DEBUG_FUNCPTR (vorbis_parse_parse_packet);
113 }
114 
115 static void
gst_vorbis_parse_init(GstVorbisParse * parse)116 gst_vorbis_parse_init (GstVorbisParse * parse)
117 {
118   parse->sinkpad =
119       gst_pad_new_from_static_template (&vorbis_parse_sink_factory, "sink");
120   gst_pad_set_chain_function (parse->sinkpad,
121       GST_DEBUG_FUNCPTR (vorbis_parse_chain));
122   gst_pad_set_event_function (parse->sinkpad,
123       GST_DEBUG_FUNCPTR (vorbis_parse_sink_event));
124   gst_element_add_pad (GST_ELEMENT (parse), parse->sinkpad);
125 
126   parse->srcpad =
127       gst_pad_new_from_static_template (&vorbis_parse_src_factory, "src");
128   gst_pad_set_query_function (parse->srcpad,
129       GST_DEBUG_FUNCPTR (vorbis_parse_src_query));
130   gst_element_add_pad (GST_ELEMENT (parse), parse->srcpad);
131 }
132 
133 static void
vorbis_parse_set_header_on_caps(GstVorbisParse * parse,GstCaps * caps)134 vorbis_parse_set_header_on_caps (GstVorbisParse * parse, GstCaps * caps)
135 {
136   GstBuffer *buf1, *buf2, *buf3;
137   GstStructure *structure;
138   GValue array = { 0 };
139   GValue value = { 0 };
140 
141   g_assert (parse);
142   g_assert (parse->streamheader);
143   g_assert (parse->streamheader->next);
144   g_assert (parse->streamheader->next->next);
145   buf1 = parse->streamheader->data;
146   g_assert (buf1);
147   buf2 = parse->streamheader->next->data;
148   g_assert (buf2);
149   buf3 = parse->streamheader->next->next->data;
150   g_assert (buf3);
151 
152   structure = gst_caps_get_structure (caps, 0);
153 
154   /* mark buffers */
155   GST_BUFFER_FLAG_SET (buf1, GST_BUFFER_FLAG_HEADER);
156   GST_BUFFER_FLAG_SET (buf2, GST_BUFFER_FLAG_HEADER);
157   GST_BUFFER_FLAG_SET (buf3, GST_BUFFER_FLAG_HEADER);
158 
159   /* put buffers in a fixed list */
160   g_value_init (&array, GST_TYPE_ARRAY);
161   g_value_init (&value, GST_TYPE_BUFFER);
162   gst_value_set_buffer (&value, buf1);
163   gst_value_array_append_value (&array, &value);
164   g_value_unset (&value);
165   g_value_init (&value, GST_TYPE_BUFFER);
166   gst_value_set_buffer (&value, buf2);
167   gst_value_array_append_value (&array, &value);
168   g_value_unset (&value);
169   g_value_init (&value, GST_TYPE_BUFFER);
170   gst_value_set_buffer (&value, buf3);
171   gst_value_array_append_value (&array, &value);
172   gst_structure_take_value (structure, "streamheader", &array);
173   g_value_unset (&value);
174 }
175 
176 static void
vorbis_parse_drain_event_queue(GstVorbisParse * parse)177 vorbis_parse_drain_event_queue (GstVorbisParse * parse)
178 {
179   while (parse->event_queue->length) {
180     GstEvent *event;
181 
182     event = GST_EVENT_CAST (g_queue_pop_head (parse->event_queue));
183     gst_pad_event_default (parse->sinkpad, GST_OBJECT_CAST (parse), event);
184   }
185 }
186 
187 static gboolean
vorbis_parse_have_header_packet(GstVorbisParse * parse,guint8 hdr_id)188 vorbis_parse_have_header_packet (GstVorbisParse * parse, guint8 hdr_id)
189 {
190   guint8 hdr;
191   GList *l;
192 
193   for (l = parse->streamheader; l != NULL; l = l->next) {
194     if (gst_buffer_extract (l->data, 0, &hdr, 1) == 1 && hdr == hdr_id)
195       return TRUE;
196   }
197 
198   return FALSE;
199 }
200 
201 static gboolean
vorbis_parse_push_headers(GstVorbisParse * parse)202 vorbis_parse_push_headers (GstVorbisParse * parse)
203 {
204   /* mark and put on caps */
205   GstCaps *caps;
206   GstBuffer *outbuf, *outbuf1, *outbuf2, *outbuf3;
207   ogg_packet packet;
208   GstMapInfo map;
209   const gchar *hdr_name;
210 
211   /* Check we have enough header packets, and the right ones */
212   hdr_name = "identification";
213   if (!vorbis_parse_have_header_packet (parse, 1))
214     goto missing_header;
215 
216   hdr_name = "comment";
217   if (!vorbis_parse_have_header_packet (parse, 3))
218     goto missing_header;
219 
220   hdr_name = "setup";
221   if (!vorbis_parse_have_header_packet (parse, 5))
222     goto missing_header;
223 
224   outbuf = GST_BUFFER_CAST (parse->streamheader->data);
225   gst_buffer_map (outbuf, &map, GST_MAP_READ);
226   packet.packet = map.data;
227   packet.bytes = map.size;
228   packet.granulepos = GST_BUFFER_OFFSET_END (outbuf);
229   packet.packetno = 1;
230   packet.e_o_s = 0;
231   packet.b_o_s = 1;
232   vorbis_synthesis_headerin (&parse->vi, &parse->vc, &packet);
233   gst_buffer_unmap (outbuf, &map);
234   parse->sample_rate = parse->vi.rate;
235   parse->channels = parse->vi.channels;
236   outbuf1 = outbuf;
237 
238   outbuf = GST_BUFFER_CAST (parse->streamheader->next->data);
239   gst_buffer_map (outbuf, &map, GST_MAP_READ);
240   packet.packet = map.data;
241   packet.bytes = map.size;
242   packet.granulepos = GST_BUFFER_OFFSET_END (outbuf);
243   packet.packetno = 2;
244   packet.e_o_s = 0;
245   packet.b_o_s = 0;
246   vorbis_synthesis_headerin (&parse->vi, &parse->vc, &packet);
247   gst_buffer_unmap (outbuf, &map);
248   outbuf2 = outbuf;
249 
250   outbuf = GST_BUFFER_CAST (parse->streamheader->next->next->data);
251   gst_buffer_map (outbuf, &map, GST_MAP_READ);
252   packet.packet = map.data;
253   packet.bytes = map.size;
254   packet.granulepos = GST_BUFFER_OFFSET_END (outbuf);
255   packet.packetno = 3;
256   packet.e_o_s = 0;
257   packet.b_o_s = 0;
258   vorbis_synthesis_headerin (&parse->vi, &parse->vc, &packet);
259   gst_buffer_unmap (outbuf, &map);
260   outbuf3 = outbuf;
261 
262   /* get the headers into the caps, passing them to vorbis as we go */
263   caps = gst_caps_new_simple ("audio/x-vorbis",
264       "rate", G_TYPE_INT, parse->sample_rate,
265       "channels", G_TYPE_INT, parse->channels, NULL);
266   vorbis_parse_set_header_on_caps (parse, caps);
267   GST_DEBUG_OBJECT (parse, "here are the caps: %" GST_PTR_FORMAT, caps);
268   gst_pad_set_caps (parse->srcpad, caps);
269   gst_caps_unref (caps);
270 
271   /* first process queued events */
272   vorbis_parse_drain_event_queue (parse);
273 
274   /* push out buffers, ignoring return value... */
275   gst_pad_push (parse->srcpad, outbuf1);
276   gst_pad_push (parse->srcpad, outbuf2);
277   gst_pad_push (parse->srcpad, outbuf3);
278 
279   g_list_free (parse->streamheader);
280   parse->streamheader = NULL;
281   return TRUE;
282 
283 /* ERRORS */
284 missing_header:
285   {
286     GST_ELEMENT_ERROR (parse, STREAM, DECODE, (NULL),
287         ("Vorbis stream is missing %s header", hdr_name));
288     return FALSE;
289   }
290 }
291 
292 static void
vorbis_parse_clear_queue(GstVorbisParse * parse)293 vorbis_parse_clear_queue (GstVorbisParse * parse)
294 {
295   while (parse->buffer_queue->length) {
296     GstBuffer *buf;
297 
298     buf = GST_BUFFER_CAST (g_queue_pop_head (parse->buffer_queue));
299     gst_buffer_unref (buf);
300   }
301   while (parse->event_queue->length) {
302     GstEvent *event;
303 
304     event = GST_EVENT_CAST (g_queue_pop_head (parse->event_queue));
305     gst_event_unref (event);
306   }
307 }
308 
309 static GstFlowReturn
vorbis_parse_push_buffer(GstVorbisParse * parse,GstBuffer * buf,gint64 granulepos)310 vorbis_parse_push_buffer (GstVorbisParse * parse, GstBuffer * buf,
311     gint64 granulepos)
312 {
313   guint64 samples;
314 
315   /* our hack as noted below */
316   samples = GST_BUFFER_OFFSET (buf);
317 
318   GST_BUFFER_OFFSET_END (buf) = granulepos;
319   GST_BUFFER_DURATION (buf) = samples * GST_SECOND / parse->sample_rate;
320   GST_BUFFER_OFFSET (buf) = granulepos * GST_SECOND / parse->sample_rate;
321   GST_BUFFER_TIMESTAMP (buf) =
322       GST_BUFFER_OFFSET (buf) - GST_BUFFER_DURATION (buf);
323 
324   return gst_pad_push (parse->srcpad, buf);
325 }
326 
327 static GstFlowReturn
vorbis_parse_drain_queue_prematurely(GstVorbisParse * parse)328 vorbis_parse_drain_queue_prematurely (GstVorbisParse * parse)
329 {
330   GstFlowReturn ret = GST_FLOW_OK;
331   gint64 granulepos = MAX (parse->prev_granulepos, 0);
332 
333   /* got an EOS event, make sure to push out any buffers that were in the queue
334    * -- won't normally be the case, but this catches the
335    * didn't-get-a-granulepos-on-the-last-packet case. Assuming a continuous
336    * stream. */
337 
338   /* if we got EOS before any buffers came, go ahead and push the other events
339    * first */
340   vorbis_parse_drain_event_queue (parse);
341 
342   while (!g_queue_is_empty (parse->buffer_queue)) {
343     GstBuffer *buf;
344 
345     buf = GST_BUFFER_CAST (g_queue_pop_head (parse->buffer_queue));
346 
347     granulepos += GST_BUFFER_OFFSET (buf);
348     ret = vorbis_parse_push_buffer (parse, buf, granulepos);
349 
350     if (ret != GST_FLOW_OK)
351       goto done;
352   }
353 
354   parse->prev_granulepos = granulepos;
355 
356 done:
357   return ret;
358 }
359 
360 static GstFlowReturn
vorbis_parse_drain_queue(GstVorbisParse * parse,gint64 granulepos)361 vorbis_parse_drain_queue (GstVorbisParse * parse, gint64 granulepos)
362 {
363   GstFlowReturn ret = GST_FLOW_OK;
364   GList *walk;
365   gint64 cur = granulepos;
366   gint64 gp;
367 
368   for (walk = parse->buffer_queue->head; walk; walk = walk->next)
369     cur -= GST_BUFFER_OFFSET (walk->data);
370 
371   if (parse->prev_granulepos != -1)
372     cur = MAX (cur, parse->prev_granulepos);
373 
374   while (!g_queue_is_empty (parse->buffer_queue)) {
375     GstBuffer *buf;
376 
377     buf = GST_BUFFER_CAST (g_queue_pop_head (parse->buffer_queue));
378 
379     cur += GST_BUFFER_OFFSET (buf);
380     gp = CLAMP (cur, 0, granulepos);
381 
382     ret = vorbis_parse_push_buffer (parse, buf, gp);
383 
384     if (ret != GST_FLOW_OK)
385       goto done;
386   }
387 
388   parse->prev_granulepos = granulepos;
389 
390 done:
391   return ret;
392 }
393 
394 static GstFlowReturn
vorbis_parse_queue_buffer(GstVorbisParse * parse,GstBuffer * buf)395 vorbis_parse_queue_buffer (GstVorbisParse * parse, GstBuffer * buf)
396 {
397   GstFlowReturn ret = GST_FLOW_OK;
398   long blocksize;
399   ogg_packet packet;
400   GstMapInfo map;
401 
402   buf = gst_buffer_make_writable (buf);
403 
404   gst_buffer_map (buf, &map, GST_MAP_READ);
405   packet.packet = map.data;
406   packet.bytes = map.size;
407   GST_DEBUG ("%p, %" G_GSIZE_FORMAT, map.data, map.size);
408   packet.granulepos = GST_BUFFER_OFFSET_END (buf);
409   packet.packetno = parse->packetno + parse->buffer_queue->length;
410   packet.e_o_s = 0;
411 
412   blocksize = vorbis_packet_blocksize (&parse->vi, &packet);
413   gst_buffer_unmap (buf, &map);
414 
415   /* temporarily store the sample count in OFFSET -- we overwrite this later */
416 
417   if (parse->prev_blocksize < 0)
418     GST_BUFFER_OFFSET (buf) = 0;
419   else
420     GST_BUFFER_OFFSET (buf) = (blocksize + parse->prev_blocksize) / 4;
421 
422   parse->prev_blocksize = blocksize;
423 
424   g_queue_push_tail (parse->buffer_queue, buf);
425 
426   if (GST_BUFFER_OFFSET_END_IS_VALID (buf))
427     ret = vorbis_parse_drain_queue (parse, GST_BUFFER_OFFSET_END (buf));
428 
429   return ret;
430 }
431 
432 static GstFlowReturn
vorbis_parse_parse_packet(GstVorbisParse * parse,GstBuffer * buf)433 vorbis_parse_parse_packet (GstVorbisParse * parse, GstBuffer * buf)
434 {
435   GstFlowReturn ret;
436   GstMapInfo map;
437   gboolean have_header;
438 
439   parse->packetno++;
440 
441   have_header = FALSE;
442   gst_buffer_map (buf, &map, GST_MAP_READ);
443   if (map.size >= 1) {
444     if (map.data[0] & 1)
445       have_header = TRUE;
446   }
447   gst_buffer_unmap (buf, &map);
448 
449   if (have_header) {
450     if (!parse->streamheader_sent) {
451       /* we need to collect the headers still */
452       /* so put it on the streamheader list and return */
453       parse->streamheader = g_list_append (parse->streamheader, buf);
454     }
455     ret = GST_FLOW_OK;
456   } else {
457     /* data packet, push the headers we collected before */
458     if (!parse->streamheader_sent) {
459       if (!vorbis_parse_push_headers (parse)) {
460         ret = GST_FLOW_ERROR;
461         goto out;
462       }
463       parse->streamheader_sent = TRUE;
464     }
465     ret = vorbis_parse_queue_buffer (parse, buf);
466   }
467 
468 out:
469 
470   return ret;
471 }
472 
473 static GstFlowReturn
vorbis_parse_chain(GstPad * pad,GstObject * parent,GstBuffer * buffer)474 vorbis_parse_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
475 {
476   GstVorbisParseClass *klass;
477   GstVorbisParse *parse;
478 
479   parse = GST_VORBIS_PARSE (parent);
480   klass = GST_VORBIS_PARSE_CLASS (G_OBJECT_GET_CLASS (parse));
481 
482   g_assert (klass->parse_packet != NULL);
483 
484   return klass->parse_packet (parse, buffer);
485 }
486 
487 static gboolean
vorbis_parse_queue_event(GstVorbisParse * parse,GstEvent * event)488 vorbis_parse_queue_event (GstVorbisParse * parse, GstEvent * event)
489 {
490   GstFlowReturn ret = TRUE;
491 
492   g_queue_push_tail (parse->event_queue, event);
493 
494   return ret;
495 }
496 
497 static gboolean
vorbis_parse_sink_event(GstPad * pad,GstObject * parent,GstEvent * event)498 vorbis_parse_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
499 {
500   gboolean ret;
501   GstVorbisParse *parse;
502 
503   parse = GST_VORBIS_PARSE (parent);
504 
505   switch (GST_EVENT_TYPE (event)) {
506     case GST_EVENT_FLUSH_STOP:
507       vorbis_parse_clear_queue (parse);
508       parse->prev_granulepos = -1;
509       parse->prev_blocksize = -1;
510       ret = gst_pad_event_default (pad, parent, event);
511       break;
512     case GST_EVENT_EOS:
513       vorbis_parse_drain_queue_prematurely (parse);
514       ret = gst_pad_event_default (pad, parent, event);
515       break;
516     default:
517       if (!parse->streamheader_sent && GST_EVENT_IS_SERIALIZED (event)
518           && GST_EVENT_TYPE (event) > GST_EVENT_CAPS)
519         ret = vorbis_parse_queue_event (parse, event);
520       else
521         ret = gst_pad_event_default (pad, parent, event);
522       break;
523   }
524 
525   return ret;
526 }
527 
528 static gboolean
vorbis_parse_convert(GstPad * pad,GstFormat src_format,gint64 src_value,GstFormat * dest_format,gint64 * dest_value)529 vorbis_parse_convert (GstPad * pad,
530     GstFormat src_format, gint64 src_value,
531     GstFormat * dest_format, gint64 * dest_value)
532 {
533   gboolean res = TRUE;
534   GstVorbisParse *parse;
535   guint64 scale = 1;
536 
537   parse = GST_VORBIS_PARSE (GST_PAD_PARENT (pad));
538 
539   /* fixme: assumes atomic access to lots of instance variables modified from
540    * the streaming thread, including 64-bit variables */
541 
542   if (parse->packetno < 4)
543     return FALSE;
544 
545   if (src_format == *dest_format) {
546     *dest_value = src_value;
547     return TRUE;
548   }
549 
550   if (parse->sinkpad == pad &&
551       (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES))
552     return FALSE;
553 
554   switch (src_format) {
555     case GST_FORMAT_TIME:
556       switch (*dest_format) {
557         case GST_FORMAT_BYTES:
558           scale = sizeof (float) * parse->vi.channels;
559         case GST_FORMAT_DEFAULT:
560           *dest_value =
561               scale * gst_util_uint64_scale_int (src_value, parse->vi.rate,
562               GST_SECOND);
563           break;
564         default:
565           res = FALSE;
566       }
567       break;
568     case GST_FORMAT_DEFAULT:
569       switch (*dest_format) {
570         case GST_FORMAT_BYTES:
571           *dest_value = src_value * sizeof (float) * parse->vi.channels;
572           break;
573         case GST_FORMAT_TIME:
574           *dest_value =
575               gst_util_uint64_scale_int (src_value, GST_SECOND, parse->vi.rate);
576           break;
577         default:
578           res = FALSE;
579       }
580       break;
581     case GST_FORMAT_BYTES:
582       switch (*dest_format) {
583         case GST_FORMAT_DEFAULT:
584           *dest_value = src_value / (sizeof (float) * parse->vi.channels);
585           break;
586         case GST_FORMAT_TIME:
587           *dest_value = gst_util_uint64_scale_int (src_value, GST_SECOND,
588               parse->vi.rate * sizeof (float) * parse->vi.channels);
589           break;
590         default:
591           res = FALSE;
592       }
593       break;
594     default:
595       res = FALSE;
596   }
597 
598   return res;
599 }
600 
601 static gboolean
vorbis_parse_src_query(GstPad * pad,GstObject * parent,GstQuery * query)602 vorbis_parse_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
603 {
604   gint64 granulepos;
605   GstVorbisParse *parse;
606   gboolean res = FALSE;
607 
608   parse = GST_VORBIS_PARSE (parent);
609 
610   switch (GST_QUERY_TYPE (query)) {
611     case GST_QUERY_POSITION:
612     {
613       GstFormat format;
614       gint64 value;
615 
616       granulepos = parse->prev_granulepos;
617 
618       gst_query_parse_position (query, &format, NULL);
619 
620       /* and convert to the final format */
621       if (!(res =
622               vorbis_parse_convert (pad, GST_FORMAT_DEFAULT, granulepos,
623                   &format, &value)))
624         goto error;
625 
626       /* fixme: support segments
627          value = (value - parse->segment_start) + parse->segment_time;
628        */
629 
630       gst_query_set_position (query, format, value);
631 
632       GST_LOG_OBJECT (parse, "query %p: peer returned granulepos: %"
633           G_GUINT64_FORMAT " - we return %" G_GUINT64_FORMAT " (format %u)",
634           query, granulepos, value, format);
635 
636       break;
637     }
638     case GST_QUERY_DURATION:
639     {
640       /* fixme: not threadsafe */
641       /* query peer for total length */
642       if (!gst_pad_is_linked (parse->sinkpad)) {
643         GST_WARNING_OBJECT (parse, "sink pad %" GST_PTR_FORMAT " is not linked",
644             parse->sinkpad);
645         goto error;
646       }
647       if (!(res = gst_pad_peer_query (parse->sinkpad, query)))
648         goto error;
649       break;
650     }
651     case GST_QUERY_CONVERT:
652     {
653       GstFormat src_fmt, dest_fmt;
654       gint64 src_val, dest_val;
655 
656       gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val);
657       if (!(res =
658               vorbis_parse_convert (pad, src_fmt, src_val, &dest_fmt,
659                   &dest_val)))
660         goto error;
661       gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
662       break;
663     }
664     default:
665       res = gst_pad_query_default (pad, parent, query);
666       break;
667   }
668   return res;
669 
670 error:
671   {
672     GST_WARNING_OBJECT (parse, "error handling query");
673     return res;
674   }
675 }
676 
677 static GstStateChangeReturn
vorbis_parse_change_state(GstElement * element,GstStateChange transition)678 vorbis_parse_change_state (GstElement * element, GstStateChange transition)
679 {
680   GstVorbisParse *parse = GST_VORBIS_PARSE (element);
681   GstStateChangeReturn ret;
682 
683   switch (transition) {
684     case GST_STATE_CHANGE_READY_TO_PAUSED:
685       vorbis_info_init (&parse->vi);
686       vorbis_comment_init (&parse->vc);
687       parse->prev_granulepos = -1;
688       parse->prev_blocksize = -1;
689       parse->packetno = 0;
690       parse->streamheader_sent = FALSE;
691       parse->buffer_queue = g_queue_new ();
692       parse->event_queue = g_queue_new ();
693       break;
694     default:
695       break;
696   }
697 
698   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
699 
700   switch (transition) {
701     case GST_STATE_CHANGE_PAUSED_TO_READY:
702       vorbis_info_clear (&parse->vi);
703       vorbis_comment_clear (&parse->vc);
704       vorbis_parse_clear_queue (parse);
705       g_queue_free (parse->buffer_queue);
706       parse->buffer_queue = NULL;
707       g_queue_free (parse->event_queue);
708       parse->event_queue = NULL;
709       break;
710     default:
711       break;
712   }
713 
714   return ret;
715 }
716