static GstStateChangeReturn gst_musepackdec_change_state (GstElement * element, GstStateChange transition) { GstMusepackDec *musepackdec = GST_MUSEPACK_DEC (element); GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: gst_segment_init (&musepackdec->segment, GST_FORMAT_DEFAULT); gst_segment_set_last_stop (&musepackdec->segment, GST_FORMAT_DEFAULT, 0); break; default: break; } if (GST_ELEMENT_CLASS (parent_class)->change_state) ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); switch (transition) { case GST_STATE_CHANGE_PAUSED_TO_READY: gst_segment_init (&musepackdec->segment, GST_FORMAT_UNDEFINED); musepackdec->offset = 0; musepackdec->rate = 0; musepackdec->bps = 0; break; default: break; } return ret; }
static GstFlowReturn gst_real_audio_demux_parse_data (GstRealAudioDemux * demux) { GstFlowReturn ret = GST_FLOW_OK; guint avail, unit_size; avail = gst_adapter_available (demux->adapter); if (demux->packet_size > 0) unit_size = demux->packet_size; else unit_size = avail & 0xfffffff0; /* round down to next multiple of 16 */ GST_LOG_OBJECT (demux, "available = %u, unit_size = %u", avail, unit_size); while (ret == GST_FLOW_OK && unit_size > 0 && avail >= unit_size) { GstClockTime ts; const guint8 *data; GstBuffer *buf = NULL; buf = gst_buffer_new_and_alloc (unit_size); gst_buffer_set_caps (buf, GST_PAD_CAPS (demux->srcpad)); data = gst_adapter_peek (demux->adapter, unit_size); memcpy (GST_BUFFER_DATA (buf), data, unit_size); gst_adapter_flush (demux->adapter, unit_size); avail -= unit_size; if (demux->need_newsegment) { gst_pad_push_event (demux->srcpad, gst_event_new_new_segment_full (FALSE, demux->segment.rate, demux->segment.applied_rate, GST_FORMAT_TIME, demux->segment.start, demux->segment.stop, demux->segment.time)); demux->need_newsegment = FALSE; } if (demux->pending_tags) { gst_element_found_tags_for_pad (GST_ELEMENT (demux), demux->srcpad, demux->pending_tags); demux->pending_tags = NULL; } if (demux->fourcc == GST_RM_AUD_DNET) { buf = gst_rm_utils_descramble_dnet_buffer (buf); } ts = gst_real_demux_get_timestamp_from_offset (demux, demux->offset); GST_BUFFER_TIMESTAMP (buf) = ts; gst_segment_set_last_stop (&demux->segment, GST_FORMAT_TIME, ts); ret = gst_pad_push (demux->srcpad, buf); } return ret; }
void QGstreamerPlayerSession::processNewBuffer(GstBuffer *buf) { GstClockTime last_stop, duration; last_stop = GST_BUFFER_TIMESTAMP (buf); if (GST_CLOCK_TIME_IS_VALID (last_stop)) { duration = GST_BUFFER_DURATION (buf); if (GST_CLOCK_TIME_IS_VALID (duration)) { last_stop += duration; } gst_segment_set_last_stop(&m_segment, m_segment.format, last_stop); } }
static GstFlowReturn gst_selector_pad_chain (GstPad * pad, GstBuffer * buf) { RsnStreamSelector *sel; GstFlowReturn res; GstPad *active_sinkpad; RsnSelectorPad *selpad; GstClockTime timestamp; GstSegment *seg; sel = RSN_STREAM_SELECTOR (gst_pad_get_parent (pad)); selpad = GST_SELECTOR_PAD_CAST (pad); seg = &selpad->segment; active_sinkpad = rsn_stream_selector_get_active (sel, pad); timestamp = GST_BUFFER_TIMESTAMP (buf); if (GST_CLOCK_TIME_IS_VALID (timestamp)) { GST_DEBUG_OBJECT (sel, "received timestamp %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp)); gst_segment_set_last_stop (seg, seg->format, timestamp); } /* Ignore buffers from pads except the selected one */ if (pad != active_sinkpad) goto ignore; /* if we have a pending segment, push it out now */ if (selpad->segment_pending) { gst_pad_push_event (sel->srcpad, gst_event_new_new_segment_full (FALSE, seg->rate, seg->applied_rate, seg->format, seg->start, seg->stop, seg->time)); selpad->segment_pending = FALSE; } /* forward */ GST_DEBUG_OBJECT (sel, "Forwarding buffer %p from pad %s:%s", buf, GST_DEBUG_PAD_NAME (pad)); res = gst_pad_push (sel->srcpad, buf); done: gst_object_unref (sel); return res; /* dropped buffers */ ignore: { GST_DEBUG_OBJECT (sel, "Ignoring buffer %p from pad %s:%s", buf, GST_DEBUG_PAD_NAME (pad)); gst_buffer_unref (buf); res = GST_FLOW_NOT_LINKED; goto done; } }
GstFlowReturn gst_vdp_mpeg_dec_push_video_buffer (GstVdpMpegDec * mpeg_dec, GstVdpVideoBuffer * buffer) { gint64 byterate; if (GST_BUFFER_TIMESTAMP (buffer) == GST_CLOCK_TIME_NONE && GST_CLOCK_TIME_IS_VALID (mpeg_dec->next_timestamp)) { GST_BUFFER_TIMESTAMP (buffer) = mpeg_dec->next_timestamp; } else if (GST_BUFFER_TIMESTAMP (buffer) == GST_CLOCK_TIME_NONE) { GST_BUFFER_TIMESTAMP (buffer) = gst_util_uint64_scale (mpeg_dec->frame_nr, GST_SECOND * mpeg_dec->fps_d, mpeg_dec->fps_n); } if (mpeg_dec->seeking) { GstEvent *event; event = gst_event_new_new_segment (FALSE, mpeg_dec->segment.rate, GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (buffer), mpeg_dec->segment.stop, GST_BUFFER_TIMESTAMP (buffer)); gst_pad_push_event (mpeg_dec->src, event); mpeg_dec->seeking = FALSE; } mpeg_dec->next_timestamp = GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer); gst_segment_set_last_stop (&mpeg_dec->segment, GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (buffer)); mpeg_dec->accumulated_duration += GST_BUFFER_DURATION (buffer); mpeg_dec->accumulated_size += GST_BUFFER_SIZE (buffer); byterate = gst_util_uint64_scale (mpeg_dec->accumulated_size, GST_SECOND, mpeg_dec->accumulated_duration); GST_DEBUG ("byterate: %" G_GINT64_FORMAT, mpeg_dec->byterate); mpeg_dec->byterate = (mpeg_dec->byterate + byterate) / 2; gst_buffer_set_caps (GST_BUFFER (buffer), GST_PAD_CAPS (mpeg_dec->src)); GST_DEBUG_OBJECT (mpeg_dec, "Pushing buffer with timestamp: %" GST_TIME_FORMAT " frame_nr: %" G_GINT64_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)), GST_BUFFER_OFFSET (buffer)); return gst_pad_push (mpeg_dec->src, GST_BUFFER (buffer)); }
gboolean gst_kate_util_decoder_base_update_segment (GstKateDecoderBase * decoder, GstElement * element, GstBuffer * buf) { gint64 clip_start = 0, clip_stop = 0; gboolean in_seg; if (decoder->kate_flushing) { GST_LOG_OBJECT (element, "Kate pad flushing, buffer ignored"); return FALSE; } if (G_LIKELY (GST_BUFFER_TIMESTAMP_IS_VALID (buf))) { GstClockTime stop; if (G_LIKELY (GST_BUFFER_DURATION_IS_VALID (buf))) stop = GST_BUFFER_TIMESTAMP (buf) + GST_BUFFER_DURATION (buf); else stop = GST_CLOCK_TIME_NONE; in_seg = gst_segment_clip (&decoder->kate_segment, GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (buf), stop, &clip_start, &clip_stop); } else { in_seg = TRUE; } if (in_seg) { if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) { gst_segment_set_last_stop (&decoder->kate_segment, GST_FORMAT_TIME, clip_start); } } else { GST_INFO_OBJECT (element, "Kate buffer not in segment, ignored"); } return in_seg; }
static GstFlowReturn gst_aiff_parse_stream_data (GstAiffParse * aiff) { GstBuffer *buf = NULL; GstFlowReturn res = GST_FLOW_OK; guint64 desired, obtained; GstClockTime timestamp, next_timestamp, duration; guint64 pos, nextpos; iterate_adapter: GST_LOG_OBJECT (aiff, "offset: %" G_GINT64_FORMAT " , end: %" G_GINT64_FORMAT " , dataleft: %" G_GINT64_FORMAT, aiff->offset, aiff->end_offset, aiff->dataleft); /* Get the next n bytes and output them */ if (aiff->dataleft == 0 || aiff->dataleft < aiff->bytes_per_sample) goto found_eos; /* scale the amount of data by the segment rate so we get equal * amounts of data regardless of the playback rate */ desired = MIN (gst_guint64_to_gdouble (aiff->dataleft), MAX_BUFFER_SIZE * aiff->segment.abs_rate); if (desired >= aiff->bytes_per_sample && aiff->bytes_per_sample > 0) desired -= (desired % aiff->bytes_per_sample); GST_LOG_OBJECT (aiff, "Fetching %" G_GINT64_FORMAT " bytes of data " "from the sinkpad", desired); if (aiff->streaming) { guint avail = gst_adapter_available (aiff->adapter); if (avail < desired) { GST_LOG_OBJECT (aiff, "Got only %d bytes of data from the sinkpad", avail); return GST_FLOW_OK; } buf = gst_adapter_take_buffer (aiff->adapter, desired); } else { if ((res = gst_pad_pull_range (aiff->sinkpad, aiff->offset, desired, &buf)) != GST_FLOW_OK) goto pull_error; } /* If we have a pending close/start segment, send it now. */ if (G_UNLIKELY (aiff->close_segment != NULL)) { gst_pad_push_event (aiff->srcpad, aiff->close_segment); aiff->close_segment = NULL; } if (G_UNLIKELY (aiff->start_segment != NULL)) { gst_pad_push_event (aiff->srcpad, aiff->start_segment); aiff->start_segment = NULL; } obtained = GST_BUFFER_SIZE (buf); /* our positions in bytes */ pos = aiff->offset - aiff->datastart; nextpos = pos + obtained; /* update offsets, does not overflow. */ GST_BUFFER_OFFSET (buf) = pos / aiff->bytes_per_sample; GST_BUFFER_OFFSET_END (buf) = nextpos / aiff->bytes_per_sample; if (aiff->bps > 0) { /* and timestamps if we have a bitrate, be careful for overflows */ timestamp = uint64_ceiling_scale (pos, GST_SECOND, (guint64) aiff->bps); next_timestamp = uint64_ceiling_scale (nextpos, GST_SECOND, (guint64) aiff->bps); duration = next_timestamp - timestamp; /* update current running segment position */ gst_segment_set_last_stop (&aiff->segment, GST_FORMAT_TIME, next_timestamp); } else { /* no bitrate, all we know is that the first sample has timestamp 0, all * other positions and durations have unknown timestamp. */ if (pos == 0) timestamp = 0; else timestamp = GST_CLOCK_TIME_NONE; duration = GST_CLOCK_TIME_NONE; /* update current running segment position with byte offset */ gst_segment_set_last_stop (&aiff->segment, GST_FORMAT_BYTES, nextpos); } if (aiff->discont) { GST_DEBUG_OBJECT (aiff, "marking DISCONT"); GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); aiff->discont = FALSE; } GST_BUFFER_TIMESTAMP (buf) = timestamp; GST_BUFFER_DURATION (buf) = duration; gst_buffer_set_caps (buf, aiff->caps); GST_LOG_OBJECT (aiff, "Got buffer. timestamp:%" GST_TIME_FORMAT " , duration:%" GST_TIME_FORMAT ", size:%u", GST_TIME_ARGS (timestamp), GST_TIME_ARGS (duration), GST_BUFFER_SIZE (buf)); if ((res = gst_pad_push (aiff->srcpad, buf)) != GST_FLOW_OK) goto push_error; if (obtained < aiff->dataleft) { aiff->offset += obtained; aiff->dataleft -= obtained; } else { aiff->offset += aiff->dataleft; aiff->dataleft = 0; } /* Iterate until need more data, so adapter size won't grow */ if (aiff->streaming) { GST_LOG_OBJECT (aiff, "offset: %" G_GINT64_FORMAT " , end: %" G_GINT64_FORMAT, aiff->offset, aiff->end_offset); goto iterate_adapter; } return res; /* ERROR */ found_eos: { GST_DEBUG_OBJECT (aiff, "found EOS"); return GST_FLOW_UNEXPECTED; } pull_error: { /* check if we got EOS */ if (res == GST_FLOW_UNEXPECTED) goto found_eos; GST_WARNING_OBJECT (aiff, "Error getting %" G_GINT64_FORMAT " bytes from the " "sinkpad (dataleft = %" G_GINT64_FORMAT ")", desired, aiff->dataleft); return res; } push_error: { GST_INFO_OBJECT (aiff, "Error pushing on srcpad %s:%s, reason %s, is linked? = %d", GST_DEBUG_PAD_NAME (aiff->srcpad), gst_flow_get_name (res), gst_pad_is_linked (aiff->srcpad)); return res; } }
static GstFlowReturn gst_audio_rate_chain (GstPad * pad, GstBuffer * buf) { GstAudioRate *audiorate; GstClockTime in_time, in_duration, in_stop, run_time; guint64 in_offset, in_offset_end, in_samples; guint in_size; GstFlowReturn ret = GST_FLOW_OK; audiorate = GST_AUDIO_RATE (gst_pad_get_parent (pad)); /* need to be negotiated now */ if (audiorate->bytes_per_sample == 0) goto not_negotiated; /* we have a new pending segment */ if (audiorate->next_offset == -1) { gint64 pos; /* update the TIME segment */ gst_audio_rate_convert_segments (audiorate); /* first buffer, we are negotiated and we have a segment, calculate the * current expected offsets based on the segment.start, which is the first * media time of the segment and should match the media time of the first * buffer in that segment, which is the offset expressed in DEFAULT units. */ /* convert first timestamp of segment to sample position */ pos = gst_util_uint64_scale_int (audiorate->src_segment.start, audiorate->rate, GST_SECOND); GST_DEBUG_OBJECT (audiorate, "resync to offset %" G_GINT64_FORMAT, pos); audiorate->next_offset = pos; audiorate->next_ts = gst_util_uint64_scale_int (audiorate->next_offset, GST_SECOND, audiorate->rate); } audiorate->in++; in_time = GST_BUFFER_TIMESTAMP (buf); if (in_time == GST_CLOCK_TIME_NONE) { GST_DEBUG_OBJECT (audiorate, "no timestamp, using expected next time"); in_time = audiorate->next_ts; } in_size = GST_BUFFER_SIZE (buf); in_samples = in_size / audiorate->bytes_per_sample; /* get duration from the size because we can and it's more accurate */ in_duration = gst_util_uint64_scale_int (in_samples, GST_SECOND, audiorate->rate); in_stop = in_time + in_duration; /* Figure out the total accumulated segment time. */ run_time = in_time + audiorate->src_segment.accum; /* calculate the buffer offset */ in_offset = gst_util_uint64_scale_int (run_time, audiorate->rate, GST_SECOND); in_offset_end = in_offset + in_samples; GST_LOG_OBJECT (audiorate, "in_time:%" GST_TIME_FORMAT ", run_time:%" GST_TIME_FORMAT ", in_duration:%" GST_TIME_FORMAT ", in_size:%u, in_offset:%lld, in_offset_end:%lld" ", ->next_offset:%lld", GST_TIME_ARGS (in_time), GST_TIME_ARGS (run_time), GST_TIME_ARGS (in_duration), in_size, in_offset, in_offset_end, audiorate->next_offset); /* do we need to insert samples */ if (in_offset > audiorate->next_offset) { GstBuffer *fill; gint fillsize; guint64 fillsamples; /* We don't want to allocate a single unreasonably huge buffer - it might be hundreds of megabytes. So, limit each output buffer to one second of audio */ fillsamples = in_offset - audiorate->next_offset; while (fillsamples > 0) { guint64 cursamples = MIN (fillsamples, audiorate->rate); fillsamples -= cursamples; fillsize = cursamples * audiorate->bytes_per_sample; fill = gst_buffer_new_and_alloc (fillsize); /* FIXME, 0 might not be the silence byte for the negotiated format. */ memset (GST_BUFFER_DATA (fill), 0, fillsize); GST_DEBUG_OBJECT (audiorate, "inserting %lld samples", cursamples); GST_BUFFER_OFFSET (fill) = audiorate->next_offset; audiorate->next_offset += cursamples; GST_BUFFER_OFFSET_END (fill) = audiorate->next_offset; /* Use next timestamp, then calculate following timestamp based on * offset to get duration. Neccesary complexity to get 'perfect' * streams */ GST_BUFFER_TIMESTAMP (fill) = audiorate->next_ts; audiorate->next_ts = gst_util_uint64_scale_int (audiorate->next_offset, GST_SECOND, audiorate->rate); GST_BUFFER_DURATION (fill) = audiorate->next_ts - GST_BUFFER_TIMESTAMP (fill); /* we created this buffer to fill a gap */ GST_BUFFER_FLAG_SET (fill, GST_BUFFER_FLAG_GAP); /* set discont if it's pending, this is mostly done for the first buffer * and after a flushing seek */ if (audiorate->discont) { GST_BUFFER_FLAG_SET (fill, GST_BUFFER_FLAG_DISCONT); audiorate->discont = FALSE; } gst_buffer_set_caps (fill, GST_PAD_CAPS (audiorate->srcpad)); ret = gst_pad_push (audiorate->srcpad, fill); if (ret != GST_FLOW_OK) goto beach; audiorate->out++; audiorate->add += cursamples; if (!audiorate->silent) g_object_notify (G_OBJECT (audiorate), "add"); } } else if (in_offset < audiorate->next_offset) { /* need to remove samples */ if (in_offset_end <= audiorate->next_offset) { guint64 drop = in_size / audiorate->bytes_per_sample; audiorate->drop += drop; GST_DEBUG_OBJECT (audiorate, "dropping %lld samples", drop); /* we can drop the buffer completely */ gst_buffer_unref (buf); if (!audiorate->silent) g_object_notify (G_OBJECT (audiorate), "drop"); goto beach; } else { guint64 truncsamples; guint truncsize, leftsize; GstBuffer *trunc; /* truncate buffer */ truncsamples = audiorate->next_offset - in_offset; truncsize = truncsamples * audiorate->bytes_per_sample; leftsize = in_size - truncsize; trunc = gst_buffer_create_sub (buf, truncsize, leftsize); gst_buffer_unref (buf); buf = trunc; gst_buffer_set_caps (buf, GST_PAD_CAPS (audiorate->srcpad)); audiorate->drop += truncsamples; } } /* Now calculate parameters for whichever buffer (either the original * or truncated one) we're pushing. */ GST_BUFFER_OFFSET (buf) = audiorate->next_offset; GST_BUFFER_OFFSET_END (buf) = in_offset_end; GST_BUFFER_TIMESTAMP (buf) = audiorate->next_ts; audiorate->next_ts = gst_util_uint64_scale_int (in_offset_end, GST_SECOND, audiorate->rate); GST_BUFFER_DURATION (buf) = audiorate->next_ts - GST_BUFFER_TIMESTAMP (buf); if (audiorate->discont) { /* we need to output a discont buffer, do so now */ GST_DEBUG_OBJECT (audiorate, "marking DISCONT on output buffer"); buf = gst_buffer_make_metadata_writable (buf); GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); audiorate->discont = FALSE; } else if (GST_BUFFER_IS_DISCONT (buf)) { /* else we make everything continuous so we can safely remove the DISCONT * flag from the buffer if there was one */ GST_DEBUG_OBJECT (audiorate, "removing DISCONT from buffer"); buf = gst_buffer_make_metadata_writable (buf); GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT); } /* set last_stop on segment */ gst_segment_set_last_stop (&audiorate->src_segment, GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (buf) + GST_BUFFER_DURATION (buf)); ret = gst_pad_push (audiorate->srcpad, buf); audiorate->out++; audiorate->next_offset = in_offset_end; beach: gst_object_unref (audiorate); return ret; /* ERRORS */ not_negotiated: { GST_ELEMENT_ERROR (audiorate, STREAM, FORMAT, (NULL), ("pipeline error, format was not negotiated")); return GST_FLOW_NOT_NEGOTIATED; } }
static gboolean gst_musepack_stream_init (GstMusepackDec * musepackdec) { mpc_streaminfo i; GstTagList *tags; GstCaps *caps; /* set up reading */ gst_musepack_init_reader (musepackdec->r, musepackdec); #ifdef MPC_IS_OLD_API /* streaminfo */ mpc_streaminfo_init (&i); if (mpc_streaminfo_read (&i, musepackdec->r) < 0) { GST_ELEMENT_ERROR (musepackdec, STREAM, WRONG_TYPE, (NULL), (NULL)); return FALSE; } /* decoding */ mpc_decoder_setup (musepackdec->d, musepackdec->r); mpc_decoder_scale_output (musepackdec->d, 1.0); if (!mpc_decoder_initialize (musepackdec->d, &i)) { GST_ELEMENT_ERROR (musepackdec, STREAM, WRONG_TYPE, (NULL), (NULL)); return FALSE; } #else musepackdec->d = mpc_demux_init (musepackdec->r); if (!musepackdec->d) { GST_ELEMENT_ERROR (musepackdec, STREAM, WRONG_TYPE, (NULL), (NULL)); return FALSE; } mpc_demux_get_info (musepackdec->d, &i); #endif /* capsnego */ caps = gst_caps_from_string (BASE_CAPS); gst_caps_set_simple (caps, "endianness", G_TYPE_INT, G_BYTE_ORDER, "channels", G_TYPE_INT, i.channels, "rate", G_TYPE_INT, i.sample_freq, NULL); gst_pad_use_fixed_caps (musepackdec->srcpad); if (!gst_pad_set_caps (musepackdec->srcpad, caps)) { GST_ELEMENT_ERROR (musepackdec, CORE, NEGOTIATION, (NULL), (NULL)); return FALSE; } g_atomic_int_set (&musepackdec->bps, 4 * i.channels); g_atomic_int_set (&musepackdec->rate, i.sample_freq); gst_segment_set_last_stop (&musepackdec->segment, GST_FORMAT_DEFAULT, 0); gst_segment_set_duration (&musepackdec->segment, GST_FORMAT_DEFAULT, mpc_streaminfo_get_length_samples (&i)); /* send basic tags */ tags = gst_tag_list_new (); gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_AUDIO_CODEC, "Musepack", NULL); if (i.encoder[0] != '\0' && i.encoder_version > 0) { gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER, i.encoder, GST_TAG_ENCODER_VERSION, i.encoder_version, NULL); } if (i.bitrate > 0) { gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE, i.bitrate, NULL); } else if (i.average_bitrate > 0.0) { gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE, (guint) i.average_bitrate, NULL); } if (i.gain_title != 0 || i.gain_album != 0) { gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_TRACK_GAIN, (gdouble) i.gain_title / 100.0, GST_TAG_ALBUM_GAIN, (gdouble) i.gain_album / 100.0, NULL); } if (i.peak_title != 0 && i.peak_title != 32767 && i.peak_album != 0 && i.peak_album != 32767) { gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_TRACK_PEAK, (gdouble) i.peak_title / 32767.0, GST_TAG_ALBUM_PEAK, (gdouble) i.peak_album / 32767.0, NULL); } GST_LOG_OBJECT (musepackdec, "Posting tags: %" GST_PTR_FORMAT, tags); gst_element_found_tags (GST_ELEMENT (musepackdec), tags); return TRUE; }
static gboolean gst_musepackdec_handle_seek_event (GstMusepackDec * dec, GstEvent * event) { GstSeekType start_type, stop_type; GstSeekFlags flags; GstSegment segment; GstFormat format; gboolean flush; gdouble rate; gint64 start, stop; gint samplerate; gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start, &stop_type, &stop); if (format != GST_FORMAT_TIME && format != GST_FORMAT_DEFAULT) { GST_DEBUG_OBJECT (dec, "seek failed: only TIME or DEFAULT format allowed"); return FALSE; } samplerate = g_atomic_int_get (&dec->rate); if (format == GST_FORMAT_TIME) { if (start_type != GST_SEEK_TYPE_NONE) start = gst_util_uint64_scale_int (start, samplerate, GST_SECOND); if (stop_type != GST_SEEK_TYPE_NONE) stop = gst_util_uint64_scale_int (stop, samplerate, GST_SECOND); } flush = ((flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH); if (flush) gst_pad_push_event (dec->srcpad, gst_event_new_flush_start ()); else gst_pad_pause_task (dec->sinkpad); /* not _stop_task()? */ GST_PAD_STREAM_LOCK (dec->sinkpad); /* operate on segment copy until we know the seek worked */ segment = dec->segment; gst_segment_set_seek (&segment, rate, GST_FORMAT_DEFAULT, flags, start_type, start, stop_type, stop, NULL); gst_pad_push_event (dec->sinkpad, gst_event_new_flush_stop ()); GST_DEBUG_OBJECT (dec, "segment: [%" G_GINT64_FORMAT "-%" G_GINT64_FORMAT "] = [%" GST_TIME_FORMAT "-%" GST_TIME_FORMAT "]", segment.start, segment.stop, GST_TIME_ARGS (segment.start * GST_SECOND / dec->rate), GST_TIME_ARGS (segment.stop * GST_SECOND / dec->rate)); GST_DEBUG_OBJECT (dec, "performing seek to sample %" G_GINT64_FORMAT, segment.start); if (segment.start < 0 || segment.start >= segment.duration) { GST_WARNING_OBJECT (dec, "seek out of bounds"); goto failed; } #ifdef MPC_IS_OLD_API if (!mpc_decoder_seek_sample (dec->d, segment.start)) goto failed; #else if (mpc_demux_seek_sample (dec->d, segment.start) != MPC_STATUS_OK) goto failed; #endif if ((flags & GST_SEEK_FLAG_SEGMENT) == GST_SEEK_FLAG_SEGMENT) { GST_DEBUG_OBJECT (dec, "posting SEGMENT_START message"); gst_element_post_message (GST_ELEMENT (dec), gst_message_new_segment_start (GST_OBJECT (dec), GST_FORMAT_TIME, gst_util_uint64_scale_int (segment.start, GST_SECOND, dec->rate))); } if (flush) { gst_pad_push_event (dec->srcpad, gst_event_new_flush_stop ()); } gst_segment_set_last_stop (&segment, GST_FORMAT_DEFAULT, segment.start); dec->segment = segment; gst_musepackdec_send_newsegment (dec); GST_DEBUG_OBJECT (dec, "seek successful"); gst_pad_start_task (dec->sinkpad, (GstTaskFunction) gst_musepackdec_loop, dec->sinkpad); GST_PAD_STREAM_UNLOCK (dec->sinkpad); return TRUE; failed: { GST_WARNING_OBJECT (dec, "seek failed"); GST_PAD_STREAM_UNLOCK (dec->sinkpad); return FALSE; } }
static GstFlowReturn gst_selector_pad_chain (GstPad * pad, GstBuffer * buf) { GstInputSelector *sel; GstFlowReturn res; GstPad *active_sinkpad; GstPad *prev_active_sinkpad; GstSelectorPad *selpad; GstClockTime start_time; GstSegment *seg; GstEvent *close_event = NULL, *start_event = NULL; GstCaps *caps; sel = GST_INPUT_SELECTOR (gst_pad_get_parent (pad)); selpad = GST_SELECTOR_PAD_CAST (pad); seg = &selpad->segment; GST_INPUT_SELECTOR_LOCK (sel); /* wait or check for flushing */ if (gst_input_selector_wait (sel, pad)) goto flushing; GST_LOG_OBJECT (pad, "getting active pad"); prev_active_sinkpad = sel->active_sinkpad; active_sinkpad = gst_input_selector_activate_sinkpad (sel, pad); /* update the segment on the srcpad */ start_time = GST_BUFFER_TIMESTAMP (buf); if (GST_CLOCK_TIME_IS_VALID (start_time)) { GST_LOG_OBJECT (pad, "received start time %" GST_TIME_FORMAT, GST_TIME_ARGS (start_time)); if (GST_BUFFER_DURATION_IS_VALID (buf)) GST_LOG_OBJECT (pad, "received end time %" GST_TIME_FORMAT, GST_TIME_ARGS (start_time + GST_BUFFER_DURATION (buf))); GST_OBJECT_LOCK (pad); gst_segment_set_last_stop (seg, seg->format, start_time); GST_OBJECT_UNLOCK (pad); } /* Ignore buffers from pads except the selected one */ if (pad != active_sinkpad) goto ignore; if (G_UNLIKELY (sel->pending_close)) { GstSegment *cseg = &sel->segment; GST_DEBUG_OBJECT (sel, "pushing close NEWSEGMENT update %d, rate %lf, applied rate %lf, " "format %d, " "%" G_GINT64_FORMAT " -- %" G_GINT64_FORMAT ", time %" G_GINT64_FORMAT, TRUE, cseg->rate, cseg->applied_rate, cseg->format, cseg->start, cseg->stop, cseg->time); /* create update segment */ close_event = gst_event_new_new_segment_full (TRUE, cseg->rate, cseg->applied_rate, cseg->format, cseg->start, cseg->stop, cseg->time); sel->pending_close = FALSE; } /* if we have a pending segment, push it out now */ if (G_UNLIKELY (selpad->segment_pending)) { GST_DEBUG_OBJECT (pad, "pushing pending NEWSEGMENT update %d, rate %lf, applied rate %lf, " "format %d, " "%" G_GINT64_FORMAT " -- %" G_GINT64_FORMAT ", time %" G_GINT64_FORMAT, FALSE, seg->rate, seg->applied_rate, seg->format, seg->start, seg->stop, seg->time); start_event = gst_event_new_new_segment_full (FALSE, seg->rate, seg->applied_rate, seg->format, seg->start, seg->stop, seg->time); selpad->segment_pending = FALSE; } GST_INPUT_SELECTOR_UNLOCK (sel); if (prev_active_sinkpad != active_sinkpad && pad == active_sinkpad) g_object_notify (G_OBJECT (sel), "active-pad"); if (close_event) gst_pad_push_event (sel->srcpad, close_event); if (start_event) gst_pad_push_event (sel->srcpad, start_event); if (selpad->discont) { buf = gst_buffer_make_metadata_writable (buf); GST_DEBUG_OBJECT (pad, "Marking discont buffer %p", buf); GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); selpad->discont = FALSE; } /* forward */ GST_LOG_OBJECT (pad, "Forwarding buffer %p", buf); if ((caps = GST_BUFFER_CAPS (buf))) { if (GST_PAD_CAPS (sel->srcpad) != caps) gst_pad_set_caps (sel->srcpad, caps); } res = gst_pad_push (sel->srcpad, buf); done: gst_object_unref (sel); return res; /* dropped buffers */ ignore: { GST_DEBUG_OBJECT (pad, "Pad not active, discard buffer %p", buf); /* when we drop a buffer, we're creating a discont on this pad */ selpad->discont = TRUE; GST_INPUT_SELECTOR_UNLOCK (sel); gst_buffer_unref (buf); /* figure out what to return upstream */ GST_OBJECT_LOCK (selpad); if (selpad->always_ok) res = GST_FLOW_OK; else res = GST_FLOW_NOT_LINKED; GST_OBJECT_UNLOCK (selpad); goto done; } flushing: { GST_DEBUG_OBJECT (pad, "We are flushing, discard buffer %p", buf); GST_INPUT_SELECTOR_UNLOCK (sel); gst_buffer_unref (buf); res = GST_FLOW_WRONG_STATE; goto done; } }
static GstFlowReturn fs_funnel_chain (GstPad * pad, GstBuffer * buffer) { GstFlowReturn res; FsFunnel *funnel = FS_FUNNEL (gst_pad_get_parent (pad)); FsFunnelPadPrivate *priv = gst_pad_get_element_private (pad); GstEvent *event = NULL; GstClockTime newts; GstCaps *padcaps; GST_DEBUG_OBJECT (funnel, "received buffer %p", buffer); GST_OBJECT_LOCK (funnel); if (priv->segment.format == GST_FORMAT_UNDEFINED) { GST_WARNING_OBJECT (funnel, "Got buffer without segment," " setting segment [0,inf["); gst_segment_set_newsegment_full (&priv->segment, FALSE, 1.0, 1.0, GST_FORMAT_TIME, 0, -1, 0); } if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (buffer))) gst_segment_set_last_stop (&priv->segment, priv->segment.format, GST_BUFFER_TIMESTAMP (buffer)); newts = gst_segment_to_running_time (&priv->segment, priv->segment.format, GST_BUFFER_TIMESTAMP (buffer)); if (newts != GST_BUFFER_TIMESTAMP (buffer)) { buffer = gst_buffer_make_metadata_writable (buffer); GST_BUFFER_TIMESTAMP (buffer) = newts; } if (!funnel->has_segment) { event = gst_event_new_new_segment_full (FALSE, 1.0, 1.0, GST_FORMAT_TIME, 0, -1, 0); funnel->has_segment = TRUE; } GST_OBJECT_UNLOCK (funnel); if (event) { if (!gst_pad_push_event (funnel->srcpad, event)) GST_WARNING_OBJECT (funnel, "Could not push out newsegment event"); } GST_OBJECT_LOCK (pad); padcaps = GST_PAD_CAPS (funnel->srcpad); GST_OBJECT_UNLOCK (pad); if (GST_BUFFER_CAPS (buffer) && GST_BUFFER_CAPS (buffer) != padcaps) { if (!gst_pad_set_caps (funnel->srcpad, GST_BUFFER_CAPS (buffer))) { res = GST_FLOW_NOT_NEGOTIATED; goto out; } } res = gst_pad_push (funnel->srcpad, buffer); GST_LOG_OBJECT (funnel, "handled buffer %s", gst_flow_get_name (res)); out: gst_object_unref (funnel); return res; }
static GstFlowReturn handle_buffer (GstSubParse * self, GstBuffer * buf) { GstFlowReturn ret = GST_FLOW_OK; GstCaps *caps = NULL; gchar *line, *subtitle; feed_textbuf (self, buf); /* make sure we know the format */ if (G_UNLIKELY (self->parser_type == GST_SUB_PARSE_FORMAT_UNKNOWN)) { if (!(caps = gst_sub_parse_format_autodetect (self))) { return GST_FLOW_UNEXPECTED; } if (!gst_pad_set_caps (self->srcpad, caps)) { gst_caps_unref (caps); return GST_FLOW_UNEXPECTED; } gst_caps_unref (caps); } while ((line = get_next_line (self)) && !self->flushing) { /* Set segment on our parser state machine */ self->state.segment = &self->segment; /* Now parse the line, out of segment lines will just return NULL */ GST_LOG_OBJECT (self, "Parsing line '%s'", line); subtitle = self->parse_line (&self->state, line); g_free (line); if (subtitle) { guint subtitle_len = strlen (subtitle); /* +1 for terminating NUL character */ ret = gst_pad_alloc_buffer_and_set_caps (self->srcpad, GST_BUFFER_OFFSET_NONE, subtitle_len + 1, GST_PAD_CAPS (self->srcpad), &buf); if (ret == GST_FLOW_OK) { /* copy terminating NUL character as well */ memcpy (GST_BUFFER_DATA (buf), subtitle, subtitle_len + 1); GST_BUFFER_SIZE (buf) = subtitle_len; GST_BUFFER_TIMESTAMP (buf) = self->state.start_time; GST_BUFFER_DURATION (buf) = self->state.duration; /* in some cases (e.g. tmplayer) we can only determine the duration * of a text chunk from the timestamp of the next text chunk; in those * cases, we probably want to limit the duration to something * reasonable, so we don't end up showing some text for e.g. 40 seconds * just because nothing else is being said during that time */ if (self->state.max_duration > 0 && GST_BUFFER_DURATION_IS_VALID (buf)) { if (GST_BUFFER_DURATION (buf) > self->state.max_duration) GST_BUFFER_DURATION (buf) = self->state.max_duration; } gst_segment_set_last_stop (&self->segment, GST_FORMAT_TIME, self->state.start_time); GST_DEBUG_OBJECT (self, "Sending text '%s', %" GST_TIME_FORMAT " + %" GST_TIME_FORMAT, subtitle, GST_TIME_ARGS (self->state.start_time), GST_TIME_ARGS (self->state.duration)); ret = gst_pad_push (self->srcpad, buf); } /* move this forward (the tmplayer parser needs this) */ if (self->state.duration != GST_CLOCK_TIME_NONE) self->state.start_time += self->state.duration; g_free (subtitle); subtitle = NULL; if (ret != GST_FLOW_OK) { GST_DEBUG_OBJECT (self, "flow: %s", gst_flow_get_name (ret)); break; } } } return ret; }
/* For each buffer we receive we check if our collected condition is reached * and if so we call the collected function. When this is done we check if * data has been unqueued. If data is still queued we wait holding the stream * lock to make sure no EOS event can happen while we are ready to be * collected */ static GstFlowReturn gst_collect_pads_chain (GstPad * pad, GstBuffer * buffer) { GstCollectData *data; GstCollectPads *pads; GstCollectPadsPrivate *priv; GstFlowReturn ret; GST_DEBUG ("Got buffer for pad %s:%s", GST_DEBUG_PAD_NAME (pad)); /* some magic to get the managing collect_pads */ GST_OBJECT_LOCK (pad); data = (GstCollectData *) gst_pad_get_element_private (pad); if (G_UNLIKELY (data == NULL)) goto no_data; ref_data (data); GST_OBJECT_UNLOCK (pad); pads = data->collect; priv = pads->abidata.ABI.priv; GST_OBJECT_LOCK (pads); /* if not started, bail out */ if (G_UNLIKELY (!pads->started)) goto not_started; /* check if this pad is flushing */ if (G_UNLIKELY (data->abidata.ABI.flushing)) goto flushing; /* pad was EOS, we can refuse this data */ if (G_UNLIKELY (data->abidata.ABI.eos)) goto unexpected; /* see if we need to clip */ if (priv->clipfunc) { buffer = priv->clipfunc (pads, data, buffer, priv->clipfunc_user_data); if (G_UNLIKELY (buffer == NULL)) goto clipped; } GST_DEBUG ("Queuing buffer %p for pad %s:%s", buffer, GST_DEBUG_PAD_NAME (pad)); /* One more pad has data queued */ pads->queuedpads++; /* take ownership of the buffer */ if (data->buffer) gst_buffer_unref (data->buffer); data->buffer = buffer; buffer = NULL; /* update segment last position if in TIME */ if (G_LIKELY (data->segment.format == GST_FORMAT_TIME)) { GstClockTime timestamp = GST_BUFFER_TIMESTAMP (data->buffer); if (GST_CLOCK_TIME_IS_VALID (timestamp)) gst_segment_set_last_stop (&data->segment, GST_FORMAT_TIME, timestamp); } /* While we have data queued on this pad try to collect stuff */ do { GST_DEBUG ("Pad %s:%s checking", GST_DEBUG_PAD_NAME (pad)); /* Check if our collected condition is matched and call the collected function * if it is */ ret = gst_collect_pads_check_collected (pads); /* when an error occurs, we want to report this back to the caller ASAP * without having to block if the buffer was not popped */ if (G_UNLIKELY (ret != GST_FLOW_OK)) goto error; /* data was consumed, we can exit and accept new data */ if (data->buffer == NULL) break; /* Check if we got removed in the mean time, FIXME, this is racy. * Between this check and the _WAIT, the pad could be removed which will * makes us hang in the _WAIT. */ GST_OBJECT_LOCK (pad); if (G_UNLIKELY (gst_pad_get_element_private (pad) == NULL)) goto pad_removed; GST_OBJECT_UNLOCK (pad); GST_DEBUG ("Pad %s:%s has a buffer queued, waiting", GST_DEBUG_PAD_NAME (pad)); /* wait to be collected, this must happen from another thread triggered * by the _chain function of another pad. We release the lock so we * can get stopped or flushed as well. We can however not get EOS * because we still hold the STREAM_LOCK. */ GST_COLLECT_PADS_WAIT (pads); GST_DEBUG ("Pad %s:%s resuming", GST_DEBUG_PAD_NAME (pad)); /* after a signal, we could be stopped */ if (G_UNLIKELY (!pads->started)) goto not_started; /* check if this pad is flushing */ if (G_UNLIKELY (data->abidata.ABI.flushing)) goto flushing; } while (data->buffer != NULL); unlock_done: GST_DEBUG ("Pad %s:%s done", GST_DEBUG_PAD_NAME (pad)); GST_OBJECT_UNLOCK (pads); unref_data (data); if (buffer) gst_buffer_unref (buffer); return ret; pad_removed: { GST_WARNING ("%s got removed from collectpads", GST_OBJECT_NAME (pad)); GST_OBJECT_UNLOCK (pad); ret = GST_FLOW_NOT_LINKED; goto unlock_done; } /* ERRORS */ no_data: { GST_DEBUG ("%s got removed from collectpads", GST_OBJECT_NAME (pad)); GST_OBJECT_UNLOCK (pad); gst_buffer_unref (buffer); return GST_FLOW_NOT_LINKED; } not_started: { GST_DEBUG ("not started"); gst_collect_pads_clear (pads, data); ret = GST_FLOW_WRONG_STATE; goto unlock_done; } flushing: { GST_DEBUG ("pad %s:%s is flushing", GST_DEBUG_PAD_NAME (pad)); gst_collect_pads_clear (pads, data); ret = GST_FLOW_WRONG_STATE; goto unlock_done; } unexpected: { /* we should not post an error for this, just inform upstream that * we don't expect anything anymore */ GST_DEBUG ("pad %s:%s is eos", GST_DEBUG_PAD_NAME (pad)); ret = GST_FLOW_UNEXPECTED; goto unlock_done; } clipped: { GST_DEBUG ("clipped buffer on pad %s:%s", GST_DEBUG_PAD_NAME (pad)); ret = GST_FLOW_OK; goto unlock_done; } error: { /* we print the error, the element should post a reasonable error * message for fatal errors */ GST_DEBUG ("collect failed, reason %d (%s)", ret, gst_flow_get_name (ret)); gst_collect_pads_clear (pads, data); goto unlock_done; } }