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