static GstFlowReturn vorbis_dec_chain (GstPad * pad, GstBuffer * buffer) { GstVorbisDec *vd; GstFlowReturn result = GST_FLOW_OK; gboolean discont; vd = GST_VORBIS_DEC (gst_pad_get_parent (pad)); discont = GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT); /* resync on DISCONT */ if (G_UNLIKELY (discont)) { GST_DEBUG_OBJECT (vd, "received DISCONT buffer"); vd->last_timestamp = GST_CLOCK_TIME_NONE; #ifdef HAVE_VORBIS_SYNTHESIS_RESTART vorbis_synthesis_restart (&vd->vd); #endif vd->discont = TRUE; } if (vd->segment.rate >= 0.0) result = vorbis_dec_chain_forward (vd, discont, buffer); else result = vorbis_dec_chain_reverse (vd, discont, buffer); gst_object_unref (vd); return result; }
static gboolean vorbis_dec_src_event (GstPad * pad, GstEvent * event) { gboolean res = TRUE; GstVorbisDec *dec; dec = GST_VORBIS_DEC (gst_pad_get_parent (pad)); if (G_UNLIKELY (dec == NULL)) { gst_event_unref (event); return FALSE; } switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEEK: { GstFormat format, tformat; gdouble rate; GstEvent *real_seek; GstSeekFlags flags; GstSeekType cur_type, stop_type; gint64 cur, stop; gint64 tcur, tstop; guint32 seqnum; gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, &stop_type, &stop); seqnum = gst_event_get_seqnum (event); gst_event_unref (event); /* First bring the requested format to time */ tformat = GST_FORMAT_TIME; if (!(res = vorbis_dec_convert (pad, format, cur, &tformat, &tcur))) goto convert_error; if (!(res = vorbis_dec_convert (pad, format, stop, &tformat, &tstop))) goto convert_error; /* then seek with time on the peer */ real_seek = gst_event_new_seek (rate, GST_FORMAT_TIME, flags, cur_type, tcur, stop_type, tstop); gst_event_set_seqnum (real_seek, seqnum); res = gst_pad_push_event (dec->sinkpad, real_seek); break; } default: res = gst_pad_push_event (dec->sinkpad, event); break; } done: gst_object_unref (dec); return res; /* ERRORS */ convert_error: { GST_DEBUG_OBJECT (dec, "cannot convert start/stop for seek"); goto done; } }
static void vorbis_dec_flush (GstAudioDecoder * dec, gboolean hard) { #ifdef HAVE_VORBIS_SYNTHESIS_RESTART GstVorbisDec *vd = GST_VORBIS_DEC (dec); vorbis_synthesis_restart (&vd->vd); #endif }
static gboolean vorbis_dec_start (GstAudioDecoder * dec) { GstVorbisDec *vd = GST_VORBIS_DEC (dec); GST_DEBUG_OBJECT (dec, "start"); vorbis_info_init (&vd->vi); vorbis_comment_init (&vd->vc); vd->initialized = FALSE; return TRUE; }
static void vorbis_dec_finalize (GObject * object) { /* Release any possibly allocated libvorbis data. * _clear functions can safely be called multiple times */ GstVorbisDec *vd = GST_VORBIS_DEC (object); vorbis_block_clear (&vd->vb); vorbis_dsp_clear (&vd->vd); vorbis_comment_clear (&vd->vc); vorbis_info_clear (&vd->vi); G_OBJECT_CLASS (parent_class)->finalize (object); }
static gboolean vorbis_dec_stop (GstAudioDecoder * dec) { GstVorbisDec *vd = GST_VORBIS_DEC (dec); GST_DEBUG_OBJECT (dec, "stop"); vd->initialized = FALSE; #ifndef USE_TREMOLO vorbis_block_clear (&vd->vb); #endif vorbis_dsp_clear (&vd->vd); vorbis_comment_clear (&vd->vc); vorbis_info_clear (&vd->vi); return TRUE; }
static GstStateChangeReturn vorbis_dec_change_state (GstElement * element, GstStateChange transition) { GstVorbisDec *vd = GST_VORBIS_DEC (element); GstStateChangeReturn res; switch (transition) { case GST_STATE_CHANGE_NULL_TO_READY: break; case GST_STATE_CHANGE_READY_TO_PAUSED: vorbis_info_init (&vd->vi); vorbis_comment_init (&vd->vc); vd->initialized = FALSE; gst_vorbis_dec_reset (vd); break; case GST_STATE_CHANGE_PAUSED_TO_PLAYING: break; default: break; } res = parent_class->change_state (element, transition); switch (transition) { case GST_STATE_CHANGE_PLAYING_TO_PAUSED: break; case GST_STATE_CHANGE_PAUSED_TO_READY: GST_DEBUG_OBJECT (vd, "PAUSED -> READY, clearing vorbis structures"); vd->initialized = FALSE; #ifndef USE_TREMOLO vorbis_block_clear (&vd->vb); #endif vorbis_dsp_clear (&vd->vd); vorbis_comment_clear (&vd->vc); vorbis_info_clear (&vd->vi); gst_vorbis_dec_reset (vd); break; case GST_STATE_CHANGE_READY_TO_NULL: break; default: break; } return res; }
static gboolean vorbis_dec_sink_query (GstPad * pad, GstQuery * query) { GstVorbisDec *dec; gboolean res; dec = GST_VORBIS_DEC (gst_pad_get_parent (pad)); switch (GST_QUERY_TYPE (query)) { case GST_QUERY_CONVERT: { GstFormat src_fmt, dest_fmt; gint64 src_val, dest_val; gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val); if (!(res = vorbis_dec_convert (pad, src_fmt, src_val, &dest_fmt, &dest_val))) goto error; gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); break; } default: res = gst_pad_query_default (pad, query); break; } done: gst_object_unref (dec); return res; /* ERRORS */ error: { GST_DEBUG_OBJECT (dec, "error converting value"); goto done; } }
static GstFlowReturn vorbis_dec_handle_frame (GstAudioDecoder * dec, GstBuffer * buffer) { ogg_packet *packet; ogg_packet_wrapper packet_wrapper; GstFlowReturn result = GST_FLOW_OK; GstMapInfo map; GstVorbisDec *vd = GST_VORBIS_DEC (dec); /* no draining etc */ if (G_UNLIKELY (!buffer)) return GST_FLOW_OK; GST_LOG_OBJECT (vd, "got buffer %p", buffer); /* make ogg_packet out of the buffer */ gst_ogg_packet_wrapper_map (&packet_wrapper, buffer, &map); packet = gst_ogg_packet_from_wrapper (&packet_wrapper); /* set some more stuff */ packet->granulepos = -1; packet->packetno = 0; /* we don't care */ /* EOS does not matter, it is used in vorbis to implement clipping the last * block of samples based on the granulepos. We clip based on segments. */ packet->e_o_s = 0; GST_LOG_OBJECT (vd, "decode buffer of size %ld", packet->bytes); /* error out on empty header packets, but just skip empty data packets */ if (G_UNLIKELY (packet->bytes == 0)) { if (vd->initialized) goto empty_buffer; else goto empty_header; } /* switch depending on packet type */ if ((gst_ogg_packet_data (packet))[0] & 1) { if (vd->initialized) { GST_WARNING_OBJECT (vd, "Already initialized, so ignoring header packet"); goto done; } result = vorbis_handle_header_packet (vd, packet); if (result != GST_FLOW_OK) goto done; /* consumer header packet/frame */ result = gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (vd), NULL, 1); } else { GstClockTime timestamp, duration; timestamp = GST_BUFFER_TIMESTAMP (buffer); duration = GST_BUFFER_DURATION (buffer); result = vorbis_handle_data_packet (vd, packet, timestamp, duration); } done: GST_LOG_OBJECT (vd, "unmap buffer %p", buffer); gst_ogg_packet_wrapper_unmap (&packet_wrapper, buffer, &map); return result; empty_buffer: { /* don't error out here, just ignore the buffer, it's invalid for vorbis * but not fatal. */ GST_WARNING_OBJECT (vd, "empty buffer received, ignoring"); result = GST_FLOW_OK; goto done; } /* ERRORS */ empty_header: { GST_ELEMENT_ERROR (vd, STREAM, DECODE, (NULL), ("empty header received")); result = GST_FLOW_ERROR; goto done; } }
static gboolean vorbis_dec_sink_event (GstPad * pad, GstEvent * event) { gboolean ret = FALSE; GstVorbisDec *dec; dec = GST_VORBIS_DEC (gst_pad_get_parent (pad)); GST_LOG_OBJECT (dec, "handling event"); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_EOS: if (dec->segment.rate < 0.0) vorbis_dec_chain_reverse (dec, TRUE, NULL); ret = gst_pad_push_event (dec->srcpad, event); break; case GST_EVENT_FLUSH_START: ret = gst_pad_push_event (dec->srcpad, event); break; case GST_EVENT_FLUSH_STOP: /* here we must clean any state in the decoder */ #ifdef HAVE_VORBIS_SYNTHESIS_RESTART vorbis_synthesis_restart (&dec->vd); #endif gst_vorbis_dec_reset (dec); ret = gst_pad_push_event (dec->srcpad, event); break; case GST_EVENT_NEWSEGMENT: { GstFormat format; gdouble rate, arate; gint64 start, stop, time; gboolean update; gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, &start, &stop, &time); /* we need time for now */ if (format != GST_FORMAT_TIME) goto newseg_wrong_format; GST_DEBUG_OBJECT (dec, "newsegment: update %d, rate %g, arate %g, start %" GST_TIME_FORMAT ", stop %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT, update, rate, arate, GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (time)); /* now configure the values */ gst_segment_set_newsegment_full (&dec->segment, update, rate, arate, format, start, stop, time); dec->seqnum = gst_event_get_seqnum (event); if (dec->initialized) /* and forward */ ret = gst_pad_push_event (dec->srcpad, event); else { /* store it to send once we're initialized */ dec->pendingevents = g_list_append (dec->pendingevents, event); ret = TRUE; } break; } case GST_EVENT_TAG: { if (dec->initialized) /* and forward */ ret = gst_pad_push_event (dec->srcpad, event); else { /* store it to send once we're initialized */ dec->pendingevents = g_list_append (dec->pendingevents, event); ret = TRUE; } break; } default: ret = gst_pad_push_event (dec->srcpad, event); break; } done: gst_object_unref (dec); return ret; /* ERRORS */ newseg_wrong_format: { GST_DEBUG_OBJECT (dec, "received non TIME newsegment"); goto done; } }
static gboolean vorbis_dec_src_query (GstPad * pad, GstQuery * query) { GstVorbisDec *dec; gboolean res = FALSE; dec = GST_VORBIS_DEC (gst_pad_get_parent (pad)); switch (GST_QUERY_TYPE (query)) { case GST_QUERY_POSITION: { gint64 value; GstFormat format; gint64 time; gst_query_parse_position (query, &format, NULL); /* we start from the last seen time */ time = dec->last_timestamp; /* correct for the segment values */ time = gst_segment_to_stream_time (&dec->segment, GST_FORMAT_TIME, time); GST_LOG_OBJECT (dec, "query %p: our time: %" GST_TIME_FORMAT, query, GST_TIME_ARGS (time)); /* and convert to the final format */ if (!(res = vorbis_dec_convert (pad, GST_FORMAT_TIME, time, &format, &value))) goto error; gst_query_set_position (query, format, value); GST_LOG_OBJECT (dec, "query %p: we return %" G_GINT64_FORMAT " (format %u)", query, value, format); break; } case GST_QUERY_DURATION: { res = gst_pad_peer_query (dec->sinkpad, query); if (!res) goto error; break; } case GST_QUERY_CONVERT: { GstFormat src_fmt, dest_fmt; gint64 src_val, dest_val; gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val); if (!(res = vorbis_dec_convert (pad, src_fmt, src_val, &dest_fmt, &dest_val))) goto error; gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); break; } default: res = gst_pad_query_default (pad, query); break; } done: gst_object_unref (dec); return res; /* ERRORS */ error: { GST_WARNING_OBJECT (dec, "error handling query"); goto done; } }
static gboolean vorbis_dec_convert (GstPad * pad, GstFormat src_format, gint64 src_value, GstFormat * dest_format, gint64 * dest_value) { gboolean res = TRUE; GstVorbisDec *dec; guint64 scale = 1; if (src_format == *dest_format) { *dest_value = src_value; return TRUE; } dec = GST_VORBIS_DEC (gst_pad_get_parent (pad)); if (!dec->initialized) goto no_header; if (dec->sinkpad == pad && (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES)) goto no_format; switch (src_format) { case GST_FORMAT_TIME: switch (*dest_format) { case GST_FORMAT_BYTES: scale = dec->width * dec->vi.channels; case GST_FORMAT_DEFAULT: *dest_value = scale * gst_util_uint64_scale_int (src_value, dec->vi.rate, GST_SECOND); break; default: res = FALSE; } break; case GST_FORMAT_DEFAULT: switch (*dest_format) { case GST_FORMAT_BYTES: *dest_value = src_value * dec->width * dec->vi.channels; break; case GST_FORMAT_TIME: *dest_value = gst_util_uint64_scale_int (src_value, GST_SECOND, dec->vi.rate); break; default: res = FALSE; } break; case GST_FORMAT_BYTES: switch (*dest_format) { case GST_FORMAT_DEFAULT: *dest_value = src_value / (dec->width * dec->vi.channels); break; case GST_FORMAT_TIME: *dest_value = gst_util_uint64_scale_int (src_value, GST_SECOND, dec->vi.rate * dec->width * dec->vi.channels); break; default: res = FALSE; } break; default: res = FALSE; } done: gst_object_unref (dec); return res; /* ERRORS */ no_header: { GST_DEBUG_OBJECT (dec, "no header packets received"); res = FALSE; goto done; } no_format: { GST_DEBUG_OBJECT (dec, "formats unsupported"); res = FALSE; goto done; } }