static gboolean gst_sub_parse_src_event (GstPad * pad, GstEvent * event) { GstSubParse *self = GST_SUBPARSE (gst_pad_get_parent (pad)); gboolean ret = FALSE; GST_DEBUG ("Handling %s event", GST_EVENT_TYPE_NAME (event)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEEK: { GstFormat format; GstSeekType start_type, stop_type; gint64 start, stop; gdouble rate; gboolean update; gst_event_parse_seek (event, &rate, &format, &self->segment_flags, &start_type, &start, &stop_type, &stop); if (format != GST_FORMAT_TIME) { GST_WARNING_OBJECT (self, "we only support seeking in TIME format"); gst_event_unref (event); goto beach; } /* Convert that seek to a seeking in bytes at position 0, FIXME: could use an index */ ret = gst_pad_push_event (self->sinkpad, gst_event_new_seek (rate, GST_FORMAT_BYTES, self->segment_flags, GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_NONE, 0)); if (ret) { /* Apply the seek to our segment */ gst_segment_set_seek (&self->segment, rate, format, self->segment_flags, start_type, start, stop_type, stop, &update); GST_DEBUG_OBJECT (self, "segment after seek: %" GST_SEGMENT_FORMAT, &self->segment); self->next_offset = 0; self->need_segment = TRUE; } else { GST_WARNING_OBJECT (self, "seek to 0 bytes failed"); } gst_event_unref (event); break; } default: ret = gst_pad_event_default (pad, event); break; } beach: gst_object_unref (self); return ret; }
static gboolean gst_mms_prepare_seek_segment (GstBaseSrc * src, GstEvent * event, GstSegment * segment) { GstSeekType cur_type, stop_type; gint64 cur, stop; GstSeekFlags flags; GstFormat seek_format; gdouble rate; gst_event_parse_seek (event, &rate, &seek_format, &flags, &cur_type, &cur, &stop_type, &stop); if (seek_format != GST_FORMAT_BYTES && seek_format != GST_FORMAT_TIME) { GST_LOG_OBJECT (src, "Only byte or time seeking is supported"); return FALSE; } if (stop_type != GST_SEEK_TYPE_NONE) { GST_LOG_OBJECT (src, "Stop seeking not supported"); return FALSE; } if (cur_type != GST_SEEK_TYPE_NONE && cur_type != GST_SEEK_TYPE_SET) { GST_LOG_OBJECT (src, "Only absolute seeking is supported"); return FALSE; } /* We would like to convert from GST_FORMAT_TIME to GST_FORMAT_BYTES here when needed, but we cannot as to do that we need to actually do the seek, so we handle this in do_seek instead. */ /* FIXME implement relative seeking, we could do any needed relevant seeking calculations here (in seek_format metrics), before the re-init of the segment. */ gst_segment_init (segment, seek_format); gst_segment_set_seek (segment, rate, seek_format, flags, cur_type, cur, stop_type, stop, NULL); return TRUE; }
static gboolean gst_rtmp_src_prepare_seek_segment (GstBaseSrc * basesrc, GstEvent * event, GstSegment * segment) { GstRTMPSrc *src; GstSeekType cur_type, stop_type; gint64 cur, stop; GstSeekFlags flags; GstFormat format; gdouble rate; src = GST_RTMP_SRC (basesrc); gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, &stop_type, &stop); if (!src->seekable) { GST_LOG_OBJECT (src, "Not a seekable stream"); return FALSE; } if (!src->rtmp) { GST_LOG_OBJECT (src, "Not connected yet"); return FALSE; } if (format != GST_FORMAT_TIME) { GST_LOG_OBJECT (src, "Seeking only supported in TIME format"); return FALSE; } if (stop_type != GST_SEEK_TYPE_NONE) { GST_LOG_OBJECT (src, "Setting a stop position is not supported"); return FALSE; } gst_segment_init (segment, GST_FORMAT_TIME); gst_segment_set_seek (segment, rate, format, flags, cur_type, cur, stop_type, stop, NULL); return TRUE; }
static gboolean gst_mms_do_seek (GstBaseSrc * src, GstSegment * segment) { mms_off_t start; GstMMS *mmssrc = GST_MMS (src); if (segment->format == GST_FORMAT_TIME) { if (!mmsx_time_seek (NULL, mmssrc->connection, (double) segment->start / GST_SECOND)) { GST_LOG_OBJECT (mmssrc, "mmsx_time_seek() failed"); return FALSE; } start = mmsx_get_current_pos (mmssrc->connection); GST_INFO_OBJECT (mmssrc, "sought to %" GST_TIME_FORMAT ", offset after " "seek: %" G_GINT64_FORMAT, GST_TIME_ARGS (segment->start), start); } else if (segment->format == GST_FORMAT_BYTES) { start = mmsx_seek (NULL, mmssrc->connection, segment->start, SEEK_SET); /* mmsx_seek will close and reopen the connection when seeking with the mmsh protocol, if the reopening fails this is indicated with -1 */ if (start == -1) { GST_DEBUG_OBJECT (mmssrc, "connection broken during seek"); return FALSE; } GST_INFO_OBJECT (mmssrc, "sought to: %" G_GINT64_FORMAT " bytes, " "result: %" G_GINT64_FORMAT, segment->start, start); } else { GST_DEBUG_OBJECT (mmssrc, "unsupported seek segment format: %s", GST_STR_NULL (gst_format_get_name (segment->format))); return FALSE; } gst_segment_init (segment, GST_FORMAT_BYTES); gst_segment_set_seek (segment, segment->rate, GST_FORMAT_BYTES, segment->flags, GST_SEEK_TYPE_SET, start, GST_SEEK_TYPE_NONE, segment->stop, NULL); return TRUE; }
/* This function is used to perform seeks on the element in * pull mode. * * It also works when event is NULL, in which case it will just * start from the last configured segment. This technique is * used when activating the element and to perform the seek in * READY. */ static gboolean gst_aiff_parse_perform_seek (GstAiffParse * aiff, GstEvent * event) { gboolean res; gdouble rate; GstFormat format, bformat; GstSeekFlags flags; GstSeekType cur_type = GST_SEEK_TYPE_NONE, stop_type; gint64 cur, stop, upstream_size; gboolean flush; gboolean update; GstSegment seeksegment = { 0, }; gint64 last_stop; if (event) { GST_DEBUG_OBJECT (aiff, "doing seek with event"); gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, &stop_type, &stop); /* no negative rates yet */ if (rate < 0.0) goto negative_rate; if (format != aiff->segment.format) { GST_INFO_OBJECT (aiff, "converting seek-event from %s to %s", gst_format_get_name (format), gst_format_get_name (aiff->segment.format)); res = TRUE; if (cur_type != GST_SEEK_TYPE_NONE) res = gst_pad_query_convert (aiff->srcpad, format, cur, &aiff->segment.format, &cur); if (res && stop_type != GST_SEEK_TYPE_NONE) res = gst_pad_query_convert (aiff->srcpad, format, stop, &aiff->segment.format, &stop); if (!res) goto no_format; format = aiff->segment.format; } } else { GST_DEBUG_OBJECT (aiff, "doing seek without event"); flags = 0; rate = 1.0; cur_type = GST_SEEK_TYPE_SET; stop_type = GST_SEEK_TYPE_SET; } /* get flush flag */ flush = flags & GST_SEEK_FLAG_FLUSH; /* now we need to make sure the streaming thread is stopped. We do this by * either sending a FLUSH_START event downstream which will cause the * streaming thread to stop with a WRONG_STATE. * For a non-flushing seek we simply pause the task, which will happen as soon * as it completes one iteration (and thus might block when the sink is * blocking in preroll). */ if (flush) { GST_DEBUG_OBJECT (aiff, "sending flush start"); gst_pad_push_event (aiff->srcpad, gst_event_new_flush_start ()); } else { gst_pad_pause_task (aiff->sinkpad); } /* we should now be able to grab the streaming thread because we stopped it * with the above flush/pause code */ GST_PAD_STREAM_LOCK (aiff->sinkpad); /* save current position */ last_stop = aiff->segment.last_stop; GST_DEBUG_OBJECT (aiff, "stopped streaming at %" G_GINT64_FORMAT, last_stop); /* copy segment, we need this because we still need the old * segment when we close the current segment. */ memcpy (&seeksegment, &aiff->segment, sizeof (GstSegment)); /* configure the seek parameters in the seeksegment. We will then have the * right values in the segment to perform the seek */ if (event) { GST_DEBUG_OBJECT (aiff, "configuring seek"); gst_segment_set_seek (&seeksegment, rate, format, flags, cur_type, cur, stop_type, stop, &update); } /* figure out the last position we need to play. If it's configured (stop != * -1), use that, else we play until the total duration of the file */ if ((stop = seeksegment.stop) == -1) stop = seeksegment.duration; GST_DEBUG_OBJECT (aiff, "cur_type =%d", cur_type); if ((cur_type != GST_SEEK_TYPE_NONE)) { /* bring offset to bytes, if the bps is 0, we have the segment in BYTES and * we can just copy the last_stop. If not, we use the bps to convert TIME to * bytes. */ if (aiff->bps > 0) aiff->offset = uint64_ceiling_scale (seeksegment.last_stop, (guint64) aiff->bps, GST_SECOND); else aiff->offset = seeksegment.last_stop; GST_LOG_OBJECT (aiff, "offset=%" G_GUINT64_FORMAT, aiff->offset); aiff->offset -= (aiff->offset % aiff->bytes_per_sample); GST_LOG_OBJECT (aiff, "offset=%" G_GUINT64_FORMAT, aiff->offset); aiff->offset += aiff->datastart; GST_LOG_OBJECT (aiff, "offset=%" G_GUINT64_FORMAT, aiff->offset); } else { GST_LOG_OBJECT (aiff, "continue from offset=%" G_GUINT64_FORMAT, aiff->offset); } if (stop_type != GST_SEEK_TYPE_NONE) { if (aiff->bps > 0) aiff->end_offset = uint64_ceiling_scale (stop, (guint64) aiff->bps, GST_SECOND); else aiff->end_offset = stop; GST_LOG_OBJECT (aiff, "end_offset=%" G_GUINT64_FORMAT, aiff->end_offset); aiff->end_offset -= (aiff->end_offset % aiff->bytes_per_sample); GST_LOG_OBJECT (aiff, "end_offset=%" G_GUINT64_FORMAT, aiff->end_offset); aiff->end_offset += aiff->datastart; GST_LOG_OBJECT (aiff, "end_offset=%" G_GUINT64_FORMAT, aiff->end_offset); } else { GST_LOG_OBJECT (aiff, "continue to end_offset=%" G_GUINT64_FORMAT, aiff->end_offset); } /* make sure filesize is not exceeded due to rounding errors or so, * same precaution as in _stream_headers */ bformat = GST_FORMAT_BYTES; if (gst_pad_query_peer_duration (aiff->sinkpad, &bformat, &upstream_size)) aiff->end_offset = MIN (aiff->end_offset, upstream_size); /* this is the range of bytes we will use for playback */ aiff->offset = MIN (aiff->offset, aiff->end_offset); aiff->dataleft = aiff->end_offset - aiff->offset; GST_DEBUG_OBJECT (aiff, "seek: rate %lf, offset %" G_GUINT64_FORMAT ", end %" G_GUINT64_FORMAT ", segment %" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, rate, aiff->offset, aiff->end_offset, GST_TIME_ARGS (seeksegment.start), GST_TIME_ARGS (stop)); /* prepare for streaming again */ if (flush) { /* if we sent a FLUSH_START, we now send a FLUSH_STOP */ GST_DEBUG_OBJECT (aiff, "sending flush stop"); gst_pad_push_event (aiff->srcpad, gst_event_new_flush_stop ()); } else if (aiff->segment_running) { /* we are running the current segment and doing a non-flushing seek, * close the segment first based on the previous last_stop. */ GST_DEBUG_OBJECT (aiff, "closing running segment %" G_GINT64_FORMAT " to %" G_GINT64_FORMAT, aiff->segment.accum, aiff->segment.last_stop); /* queue the segment for sending in the stream thread */ if (aiff->close_segment) gst_event_unref (aiff->close_segment); aiff->close_segment = gst_event_new_new_segment (TRUE, aiff->segment.rate, aiff->segment.format, aiff->segment.accum, aiff->segment.last_stop, aiff->segment.accum); /* keep track of our last_stop */ seeksegment.accum = aiff->segment.last_stop; } /* now we did the seek and can activate the new segment values */ memcpy (&aiff->segment, &seeksegment, sizeof (GstSegment)); /* if we're doing a segment seek, post a SEGMENT_START message */ if (aiff->segment.flags & GST_SEEK_FLAG_SEGMENT) { gst_element_post_message (GST_ELEMENT_CAST (aiff), gst_message_new_segment_start (GST_OBJECT_CAST (aiff), aiff->segment.format, aiff->segment.last_stop)); } /* now create the newsegment */ GST_DEBUG_OBJECT (aiff, "Creating newsegment from %" G_GINT64_FORMAT " to %" G_GINT64_FORMAT, aiff->segment.last_stop, stop); /* store the newsegment event so it can be sent from the streaming thread. */ if (aiff->start_segment) gst_event_unref (aiff->start_segment); aiff->start_segment = gst_event_new_new_segment (FALSE, aiff->segment.rate, aiff->segment.format, aiff->segment.last_stop, stop, aiff->segment.last_stop); /* mark discont if we are going to stream from another position. */ if (last_stop != aiff->segment.last_stop) { GST_DEBUG_OBJECT (aiff, "mark DISCONT, we did a seek to another position"); aiff->discont = TRUE; } /* and start the streaming task again */ aiff->segment_running = TRUE; if (!aiff->streaming) { gst_pad_start_task (aiff->sinkpad, (GstTaskFunction) gst_aiff_parse_loop, aiff->sinkpad); } GST_PAD_STREAM_UNLOCK (aiff->sinkpad); return TRUE; /* ERRORS */ negative_rate: { GST_DEBUG_OBJECT (aiff, "negative playback rates are not supported yet."); return FALSE; } no_format: { GST_DEBUG_OBJECT (aiff, "unsupported format given, seek aborted."); return FALSE; } }
/* TODO: if we have another payload already queued for this stream and that * payload doesn't have a duration, maybe we can calculate a duration for it * (if the previous timestamp is smaller etc. etc.) */ static void gst_asf_payload_queue_for_stream (GstASFDemux * demux, AsfPayload * payload, AsfStream * stream) { GST_DEBUG_OBJECT (demux, "Got payload for stream %d ts:%" GST_TIME_FORMAT, stream->id, GST_TIME_ARGS (payload->ts)); /* remember the first timestamp in the stream */ if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (demux->first_ts) && GST_CLOCK_TIME_IS_VALID (payload->ts))) { GST_DEBUG_OBJECT (demux, "first ts: %" GST_TIME_FORMAT, GST_TIME_ARGS (payload->ts)); demux->first_ts = payload->ts; } /* make timestamps start from 0 */ if (G_LIKELY (demux->first_ts < payload->ts)) payload->ts -= demux->first_ts; else payload->ts = 0; /* remove any incomplete payloads that will never be completed */ while (stream->payloads->len > 0) { AsfPayload *prev; guint idx_last; idx_last = stream->payloads->len - 1; prev = &g_array_index (stream->payloads, AsfPayload, idx_last); if (G_UNLIKELY (gst_asf_payload_is_complete (prev))) break; GST_DEBUG_OBJECT (demux, "Dropping incomplete fragmented media object " "queued for stream %u", stream->id); gst_buffer_replace (&prev->buf, NULL); g_array_remove_index (stream->payloads, idx_last); /* there's data missing, so there's a discontinuity now */ GST_BUFFER_FLAG_SET (payload->buf, GST_BUFFER_FLAG_DISCONT); } /* If we're about to queue a key frame that is before the segment start, we * can ditch any previously queued payloads (which would also be before the * segment start). This makes sure the decoder doesn't decode more than * absolutely necessary after a seek (we don't push out payloads that are * before the segment start until we have at least one that falls within the * segment) */ if (G_UNLIKELY (GST_CLOCK_TIME_IS_VALID (payload->ts) && payload->ts < demux->segment.start && payload->keyframe)) { GST_DEBUG_OBJECT (demux, "Queueing keyframe before segment start, removing" " %u previously-queued payloads, which would be out of segment too and" " hence don't have to be decoded", stream->payloads->len); while (stream->payloads->len > 0) { AsfPayload *last; guint idx_last; idx_last = stream->payloads->len - 1; last = &g_array_index (stream->payloads, AsfPayload, idx_last); gst_buffer_replace (&last->buf, NULL); g_array_remove_index (stream->payloads, idx_last); } /* Mark discontinuity (should be done via stream->discont anyway though) */ GST_BUFFER_FLAG_SET (payload->buf, GST_BUFFER_FLAG_DISCONT); } /* remember the first queued timestamp for the segment */ if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (demux->segment_ts) && GST_CLOCK_TIME_IS_VALID (payload->ts))) { GST_DEBUG_OBJECT (demux, "segment ts: %" GST_TIME_FORMAT, GST_TIME_ARGS (payload->ts)); demux->segment_ts = payload->ts; /* always note, but only determines segment when streaming */ if (demux->streaming) gst_segment_set_seek (&demux->segment, demux->in_segment.rate, GST_FORMAT_TIME, demux->segment.flags, GST_SEEK_TYPE_SET, demux->segment_ts, GST_SEEK_TYPE_NONE, 0, NULL); } g_array_append_vals (stream->payloads, payload, 1); }
static gboolean gst_timidity_src_event (GstPad * pad, GstEvent * event) { gboolean res = FALSE; GstTimidity *timidity = GST_TIMIDITY (gst_pad_get_parent (pad)); GST_DEBUG_OBJECT (pad, "%s event received", GST_EVENT_TYPE_NAME (event)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEEK: { gdouble rate; GstFormat src_format, dst_format; GstSeekFlags flags; GstSeekType start_type, stop_type; gint64 orig_start, start, stop; gboolean flush, update; if (!timidity->song) break; gst_event_parse_seek (event, &rate, &src_format, &flags, &start_type, &orig_start, &stop_type, &stop); dst_format = GST_FORMAT_DEFAULT; gst_timidity_src_convert (timidity, src_format, orig_start, &dst_format, &start); gst_timidity_src_convert (timidity, src_format, stop, &dst_format, &stop); flush = ((flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH); if (flush) { GST_DEBUG ("performing flush"); gst_pad_push_event (timidity->srcpad, gst_event_new_flush_start ()); } else { gst_pad_stop_task (timidity->sinkpad); } GST_PAD_STREAM_LOCK (timidity->sinkpad); if (flush) { gst_pad_push_event (timidity->srcpad, gst_event_new_flush_stop ()); } gst_segment_set_seek (timidity->o_segment, rate, dst_format, flags, start_type, start, stop_type, stop, &update); if ((flags && GST_SEEK_FLAG_SEGMENT) == GST_SEEK_FLAG_SEGMENT) { GST_DEBUG_OBJECT (timidity, "received segment seek %d, %d", (gint) start_type, (gint) stop_type); } else { GST_DEBUG_OBJECT (timidity, "received normal seek %d", (gint) start_type); update = FALSE; } gst_pad_push_event (timidity->srcpad, gst_timidity_get_new_segment_event (timidity, GST_FORMAT_TIME, update)); timidity->o_seek = TRUE; gst_pad_start_task (timidity->sinkpad, (GstTaskFunction) gst_timidity_loop, timidity->sinkpad); GST_PAD_STREAM_UNLOCK (timidity->sinkpad); GST_DEBUG ("seek done"); } res = TRUE; break; default: break; } g_object_unref (timidity); return res; }
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 gboolean gst_real_audio_demux_handle_seek (GstRealAudioDemux * demux, GstEvent * event) { GstFormat format; GstSeekFlags flags; GstSeekType cur_type, stop_type; gboolean flush, update; gdouble rate; guint64 seek_pos; gint64 cur, stop; if (!demux->seekable) goto not_seekable; if (demux->byterate_num == 0 || demux->byterate_denom == 0) goto no_bitrate; gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, &stop_type, &stop); if (format != GST_FORMAT_TIME) goto only_time_format_supported; if (rate <= 0.0) goto cannot_do_backwards_playback; flush = ((flags & GST_SEEK_FLAG_FLUSH) != 0); GST_DEBUG_OBJECT (demux, "flush=%d, rate=%g", flush, rate); /* unlock streaming thread and make streaming stop */ if (flush) { gst_pad_push_event (demux->sinkpad, gst_event_new_flush_start ()); gst_pad_push_event (demux->srcpad, gst_event_new_flush_start ()); } else { gst_pad_pause_task (demux->sinkpad); } GST_PAD_STREAM_LOCK (demux->sinkpad); if (demux->segment_running && !flush) { GstEvent *newseg; newseg = gst_event_new_new_segment_full (TRUE, demux->segment.rate, demux->segment.applied_rate, GST_FORMAT_TIME, demux->segment.start, demux->segment.last_stop, demux->segment.time); GST_DEBUG_OBJECT (demux, "sending NEWSEGMENT event to close the current " "segment: %" GST_PTR_FORMAT, newseg); gst_pad_push_event (demux->srcpad, newseg); } gst_segment_set_seek (&demux->segment, rate, format, flags, cur_type, cur, stop_type, stop, &update); GST_DEBUG_OBJECT (demux, "segment: %" GST_SEGMENT_FORMAT, &demux->segment); seek_pos = gst_util_uint64_scale (demux->segment.start, demux->byterate_num, demux->byterate_denom * GST_SECOND); if (demux->packet_size > 0) { seek_pos -= seek_pos % demux->packet_size; } seek_pos += demux->data_offset; GST_DEBUG_OBJECT (demux, "seek_pos = %" G_GUINT64_FORMAT, seek_pos); /* stop flushing */ gst_pad_push_event (demux->sinkpad, gst_event_new_flush_stop ()); gst_pad_push_event (demux->srcpad, gst_event_new_flush_stop ()); demux->offset = seek_pos; demux->need_newsegment = TRUE; /* notify start of new segment */ if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT) { gst_element_post_message (GST_ELEMENT (demux), gst_message_new_segment_start (GST_OBJECT (demux), GST_FORMAT_TIME, demux->segment.last_stop)); } demux->segment_running = TRUE; /* restart our task since it might have been stopped when we did the flush */ gst_pad_start_task (demux->sinkpad, (GstTaskFunction) gst_real_audio_demux_loop, demux); /* streaming can continue now */ GST_PAD_STREAM_UNLOCK (demux->sinkpad); return TRUE; /* ERRORS */ not_seekable: { GST_DEBUG_OBJECT (demux, "seek failed: cannot seek in streaming mode"); return FALSE; } no_bitrate: { GST_DEBUG_OBJECT (demux, "seek failed: bitrate unknown"); return FALSE; } only_time_format_supported: { GST_DEBUG_OBJECT (demux, "can only seek in TIME format"); return FALSE; } cannot_do_backwards_playback: { GST_DEBUG_OBJECT (demux, "can only seek with positive rate, not %lf", rate); return FALSE; } }
static gboolean gst_raw_parse_handle_seek_pull (GstRawParse * rp, GstEvent * event) { gdouble rate; GstFormat format; GstSeekFlags flags; GstSeekType start_type, stop_type; gint64 start, stop; gint64 last_stop; gboolean ret = FALSE; gboolean flush; GstSegment seeksegment; if (event) { gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start, &stop_type, &stop); /* convert input offsets to time */ ret = gst_raw_parse_convert (rp, format, start, GST_FORMAT_TIME, &start); ret &= gst_raw_parse_convert (rp, format, stop, GST_FORMAT_TIME, &stop); if (!ret) goto convert_failed; GST_DEBUG_OBJECT (rp, "converted start - stop to time"); format = GST_FORMAT_TIME; gst_event_unref (event); } else { format = GST_FORMAT_TIME; flags = 0; } flush = ((flags & GST_SEEK_FLAG_FLUSH) != 0); /* start flushing up and downstream so that the loop function pauses and we * can acquire the STREAM_LOCK. */ if (flush) { GST_LOG_OBJECT (rp, "flushing"); gst_pad_push_event (rp->sinkpad, gst_event_new_flush_start ()); gst_pad_push_event (rp->srcpad, gst_event_new_flush_start ()); } else { GST_LOG_OBJECT (rp, "pause task"); gst_pad_pause_task (rp->sinkpad); } GST_PAD_STREAM_LOCK (rp->sinkpad); memcpy (&seeksegment, &rp->segment, sizeof (GstSegment)); if (event) { /* configure the seek values */ gst_segment_set_seek (&seeksegment, rate, format, flags, start_type, start, stop_type, stop, NULL); } /* get the desired position */ last_stop = seeksegment.last_stop; GST_LOG_OBJECT (rp, "seeking to %" GST_TIME_FORMAT, GST_TIME_ARGS (last_stop)); /* convert the desired position to bytes */ ret = gst_raw_parse_convert (rp, format, last_stop, GST_FORMAT_BYTES, &last_stop); /* prepare for streaming */ if (flush) { GST_LOG_OBJECT (rp, "stop flush"); gst_pad_push_event (rp->sinkpad, gst_event_new_flush_stop ()); gst_pad_push_event (rp->srcpad, gst_event_new_flush_stop ()); } else if (ret && rp->running) { /* we are running the current segment and doing a non-flushing seek, * close the segment first based on the last_stop. */ GST_DEBUG_OBJECT (rp, "prepare close segment %" G_GINT64_FORMAT " to %" G_GINT64_FORMAT, rp->segment.start, rp->segment.last_stop); /* queue the segment for sending in the stream thread */ if (rp->close_segment) gst_event_unref (rp->close_segment); rp->close_segment = gst_event_new_new_segment_full (TRUE, rp->segment.rate, rp->segment.applied_rate, rp->segment.format, rp->segment.start, rp->segment.last_stop, rp->segment.time); } if (ret) { /* seek done */ /* Seek on a frame boundary */ last_stop -= last_stop % rp->framesize; rp->offset = last_stop; rp->n_frames = last_stop / rp->framesize; GST_LOG_OBJECT (rp, "seeking to bytes %" G_GINT64_FORMAT, last_stop); memcpy (&rp->segment, &seeksegment, sizeof (GstSegment)); if (rp->segment.flags & GST_SEEK_FLAG_SEGMENT) { gst_element_post_message (GST_ELEMENT_CAST (rp), gst_message_new_segment_start (GST_OBJECT_CAST (rp), rp->segment.format, rp->segment.last_stop)); } /* for deriving a stop position for the playback segment from the seek * segment, we must take the duration when the stop is not set */ if ((stop = rp->segment.stop) == -1) stop = rp->segment.duration; GST_DEBUG_OBJECT (rp, "preparing newsegment from %" G_GINT64_FORMAT " to %" G_GINT64_FORMAT, rp->segment.start, stop); /* now replace the old segment so that we send it in the stream thread the * next time it is scheduled. */ if (rp->start_segment) gst_event_unref (rp->start_segment); if (rp->segment.rate >= 0.0) { /* forward, we send data from last_stop to stop */ rp->start_segment = gst_event_new_new_segment_full (FALSE, rp->segment.rate, rp->segment.applied_rate, rp->segment.format, rp->segment.last_stop, stop, rp->segment.time); } else { /* reverse, we send data from last_stop to start */ rp->start_segment = gst_event_new_new_segment_full (FALSE, rp->segment.rate, rp->segment.applied_rate, rp->segment.format, rp->segment.start, rp->segment.last_stop, rp->segment.time); } } rp->discont = TRUE; GST_LOG_OBJECT (rp, "start streaming"); rp->running = TRUE; gst_pad_start_task (rp->sinkpad, (GstTaskFunction) gst_raw_parse_loop, rp); GST_PAD_STREAM_UNLOCK (rp->sinkpad); return ret; /* ERRORS */ convert_failed: { GST_DEBUG_OBJECT (rp, "Seek failed: couldn't convert to byte positions"); return FALSE; } }
static gboolean gst_wavpack_parse_handle_seek_event (GstWavpackParse * wvparse, GstEvent * event) { GstSeekFlags seek_flags; GstSeekType start_type; GstSeekType stop_type; GstSegment segment; GstFormat format; gboolean only_update; gboolean flush, ret; gdouble speed; gint64 stop; gint64 start; /* sample we want to seek to */ gint64 byte_offset; /* byte offset the chunk we seek to starts at */ gint64 chunk_start; /* first sample in chunk we seek to */ guint rate; gint64 last_stop; if (wvparse->adapter) { GST_DEBUG_OBJECT (wvparse, "seeking in streaming mode not implemented yet"); return FALSE; } gst_event_parse_seek (event, &speed, &format, &seek_flags, &start_type, &start, &stop_type, &stop); if (format != GST_FORMAT_DEFAULT && format != GST_FORMAT_TIME) { GST_DEBUG ("seeking is only supported in TIME or DEFAULT format"); return FALSE; } if (speed < 0.0) { GST_DEBUG ("only forward playback supported, rate %f not allowed", speed); return FALSE; } GST_OBJECT_LOCK (wvparse); rate = wvparse->samplerate; if (rate == 0) { GST_OBJECT_UNLOCK (wvparse); GST_DEBUG ("haven't read header yet"); return FALSE; } /* figure out the last position we need to play. If it's configured (stop != * -1), use that, else we play until the total duration of the file */ if (stop == -1) stop = wvparse->segment.duration; /* convert from time to samples if necessary */ if (format == GST_FORMAT_TIME) { if (start_type != GST_SEEK_TYPE_NONE) start = gst_util_uint64_scale_int (start, rate, GST_SECOND); if (stop_type != GST_SEEK_TYPE_NONE) stop = gst_util_uint64_scale_int (stop, rate, GST_SECOND); } if (start < 0) { GST_OBJECT_UNLOCK (wvparse); GST_DEBUG_OBJECT (wvparse, "Invalid start sample %" G_GINT64_FORMAT, start); return FALSE; } flush = ((seek_flags & GST_SEEK_FLAG_FLUSH) != 0); /* operate on segment copy until we know the seek worked */ segment = wvparse->segment; gst_segment_set_seek (&segment, speed, GST_FORMAT_DEFAULT, seek_flags, start_type, start, stop_type, stop, &only_update); #if 0 if (only_update) { wvparse->segment = segment; gst_wavpack_parse_send_newsegment (wvparse, TRUE); goto done; } #endif gst_pad_push_event (wvparse->sinkpad, gst_event_new_flush_start ()); if (flush) { gst_pad_push_event (wvparse->srcpad, gst_event_new_flush_start ()); } else { gst_pad_pause_task (wvparse->sinkpad); } GST_PAD_STREAM_LOCK (wvparse->sinkpad); /* Save current position */ last_stop = wvparse->segment.last_stop; gst_pad_push_event (wvparse->sinkpad, gst_event_new_flush_stop ()); if (flush) { gst_pad_push_event (wvparse->srcpad, gst_event_new_flush_stop ()); } GST_DEBUG_OBJECT (wvparse, "Performing seek to %" GST_TIME_FORMAT " sample %" G_GINT64_FORMAT, GST_TIME_ARGS (segment.start * GST_SECOND / rate), start); ret = gst_wavpack_parse_scan_to_find_sample (wvparse, segment.start, &byte_offset, &chunk_start); if (ret) { GST_DEBUG_OBJECT (wvparse, "new offset: %" G_GINT64_FORMAT, byte_offset); wvparse->current_offset = byte_offset; /* we want to send a newsegment event with the actual seek position * as start, even though our first buffer might start before the * configured segment. We leave it up to the decoder or sink to crop * the output buffers accordingly */ wvparse->segment = segment; wvparse->segment.last_stop = chunk_start; wvparse->need_newsegment = TRUE; wvparse->discont = (last_stop != chunk_start) ? TRUE : FALSE; /* if we're doing a segment seek, post a SEGMENT_START message */ if (wvparse->segment.flags & GST_SEEK_FLAG_SEGMENT) { gst_element_post_message (GST_ELEMENT_CAST (wvparse), gst_message_new_segment_start (GST_OBJECT_CAST (wvparse), wvparse->segment.format, wvparse->segment.last_stop)); } } else { GST_DEBUG_OBJECT (wvparse, "seek failed: don't know where to seek to"); } GST_PAD_STREAM_UNLOCK (wvparse->sinkpad); GST_OBJECT_UNLOCK (wvparse); gst_pad_start_task (wvparse->sinkpad, (GstTaskFunction) gst_wavpack_parse_loop, wvparse); return ret; }
static gboolean normal_seek (GstVdpMpegDec * mpeg_dec, GstEvent * event) { gdouble rate; GstFormat format; GstSeekFlags flags; GstSeekType cur_type, stop_type; gint64 time_cur, bytes_cur; gint64 time_stop, bytes_stop; gboolean res; gboolean update; GstEvent *peer_event; GST_DEBUG ("normal seek"); gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &time_cur, &stop_type, &time_stop); if (format != GST_FORMAT_TIME) return FALSE; gst_segment_set_seek (&mpeg_dec->segment, rate, GST_FORMAT_TIME, flags, cur_type, time_cur, stop_type, time_stop, &update); if (update) { /* seek on bytes */ if (!gst_vdp_mpeg_dec_convert (mpeg_dec, GST_FORMAT_TIME, time_cur, GST_FORMAT_BYTES, &bytes_cur)) goto convert_failed; if (!gst_vdp_mpeg_dec_convert (mpeg_dec, GST_FORMAT_TIME, time_stop, GST_FORMAT_BYTES, &bytes_stop)) goto convert_failed; /* conversion succeeded, create the seek */ peer_event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, bytes_cur, stop_type, bytes_stop); g_mutex_lock (mpeg_dec->mutex); /* do the seek */ res = gst_pad_push_event (mpeg_dec->sink, peer_event); if (res) { mpeg_dec->state = GST_VDP_MPEG_DEC_NEED_GOP; mpeg_dec->seeking = TRUE; } g_mutex_unlock (mpeg_dec->mutex); } else { GstEvent *event; /* send segment with new rate */ event = gst_event_new_new_segment (TRUE, mpeg_dec->segment.rate, GST_FORMAT_TIME, mpeg_dec->segment.start, mpeg_dec->segment.stop, mpeg_dec->segment.time); gst_pad_push_event (mpeg_dec->src, event); res = TRUE; } return res; /* ERRORS */ convert_failed: { /* probably unsupported seek format */ GST_DEBUG_OBJECT (mpeg_dec, "failed to convert format %u into GST_FORMAT_TIME", format); return FALSE; } }
static gboolean gst_wildmidi_do_seek (GstWildmidi * wildmidi, GstEvent * event) { gdouble rate; GstFormat src_format, dst_format; GstSeekFlags flags; GstSeekType start_type, stop_type; gint64 start, stop; gboolean flush, update, accurate; gboolean res; unsigned long int sample; GstSegment *segment; if (!wildmidi->song) return FALSE; gst_event_parse_seek (event, &rate, &src_format, &flags, &start_type, &start, &stop_type, &stop); /* convert the input format to samples */ dst_format = GST_FORMAT_DEFAULT; res = TRUE; if (start_type != GST_SEEK_TYPE_NONE) { res = gst_wildmidi_src_convert (wildmidi, src_format, start, &dst_format, &start); } if (res && stop_type != GST_SEEK_TYPE_NONE) { res = gst_wildmidi_src_convert (wildmidi, src_format, stop, &dst_format, &stop); } /* unsupported format */ if (!res) return res; flush = ((flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH); accurate = ((flags & GST_SEEK_FLAG_ACCURATE) == GST_SEEK_FLAG_ACCURATE); if (flush) { GST_DEBUG ("performing flush"); gst_pad_push_event (wildmidi->srcpad, gst_event_new_flush_start ()); } else { gst_pad_stop_task (wildmidi->sinkpad); } segment = wildmidi->o_segment; GST_PAD_STREAM_LOCK (wildmidi->sinkpad); if (flush) { gst_pad_push_event (wildmidi->srcpad, gst_event_new_flush_stop ()); } /* update the segment now */ gst_segment_set_seek (segment, rate, dst_format, flags, start_type, start, stop_type, stop, &update); /* we need to seek to last_stop in the segment now, sample will be updated */ sample = segment->last_stop; GST_OBJECT_LOCK (wildmidi); if (accurate) { WildMidi_SampledSeek (wildmidi->song, &sample); } else { WildMidi_FastSeek (wildmidi->song, &sample); } GST_OBJECT_UNLOCK (wildmidi); segment->start = segment->time = segment->last_stop = sample; gst_pad_push_event (wildmidi->srcpad, gst_wildmidi_get_new_segment_event (wildmidi, GST_FORMAT_TIME)); gst_pad_start_task (wildmidi->sinkpad, (GstTaskFunction) gst_wildmidi_loop, wildmidi->sinkpad); wildmidi->discont = TRUE; GST_PAD_STREAM_UNLOCK (wildmidi->sinkpad); GST_DEBUG ("seek done"); return TRUE; }