static gboolean _send_event (GstElement * element, GstEvent * event) { GstAggregator *self = GST_AGGREGATOR (element); GST_STATE_LOCK (element); if (GST_EVENT_TYPE (event) == GST_EVENT_SEEK && GST_STATE (element) < GST_STATE_PAUSED) { gdouble rate; GstFormat fmt; GstSeekFlags flags; GstSeekType start_type, stop_type; gint64 start, stop; gst_event_parse_seek (event, &rate, &fmt, &flags, &start_type, &start, &stop_type, &stop); gst_segment_do_seek (&self->segment, rate, fmt, flags, start_type, start, stop_type, stop, NULL); self->priv->seqnum = gst_event_get_seqnum (event); GST_DEBUG_OBJECT (element, "Storing segment %" GST_PTR_FORMAT, event); } GST_STATE_UNLOCK (element); return GST_ELEMENT_CLASS (aggregator_parent_class)->send_event (element, event); }
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_do_seek (segment, rate, format, flags, cur_type, cur, stop_type, stop, NULL); return TRUE; }
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_do_seek (segment, rate, seek_format, flags, cur_type, cur, stop_type, stop, NULL); return TRUE; }
static gboolean _do_seek (GstAggregator * self, GstEvent * event) { gdouble rate; GstFormat fmt; GstSeekFlags flags; GstSeekType start_type, stop_type; gint64 start, stop; gboolean flush; gboolean res; GstAggregatorPrivate *priv = self->priv; gst_event_parse_seek (event, &rate, &fmt, &flags, &start_type, &start, &stop_type, &stop); GST_INFO_OBJECT (self, "starting SEEK"); flush = flags & GST_SEEK_FLAG_FLUSH; if (flush) { g_atomic_int_set (&priv->pending_flush_start, TRUE); g_atomic_int_set (&priv->flush_seeking, TRUE); } gst_segment_do_seek (&self->segment, rate, fmt, flags, start_type, start, stop_type, stop, NULL); /* forward the seek upstream */ res = _forward_event_to_all_sinkpads (self, event, flush); event = NULL; if (!res) { g_atomic_int_set (&priv->flush_seeking, FALSE); g_atomic_int_set (&priv->pending_flush_start, FALSE); } GST_INFO_OBJECT (self, "seek done, result: %d", res); return res; }
static gboolean gst_mms_do_seek (GstBaseSrc * src, GstSegment * segment) { gint64 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_do_seek (segment, segment->rate, GST_FORMAT_BYTES, GST_SEEK_FLAG_NONE, GST_SEEK_TYPE_SET, start, GST_SEEK_TYPE_NONE, segment->stop, NULL); 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_do_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 (TRUE)); 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 >= segment.duration) { GST_WARNING_OBJECT (dec, "seek out of bounds"); goto failed; } if (mpc_demux_seek_sample (dec->d, segment.start) != MPC_STATUS_OK) goto failed; 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 (TRUE)); } segment.position = 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, NULL); 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_type_find_element_seek (GstTypeFindElement * typefind, GstEvent * event) { GstSeekFlags flags; GstSeekType start_type, stop_type; GstFormat format; gboolean flush; gdouble rate; gint64 start, stop; GstSegment seeksegment = { 0, }; gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start, &stop_type, &stop); /* we can only seek on bytes */ if (format != GST_FORMAT_BYTES) { GST_DEBUG_OBJECT (typefind, "Can only seek on BYTES"); return FALSE; } /* copy segment, we need this because we still need the old * segment when we close the current segment. */ memcpy (&seeksegment, &typefind->segment, sizeof (GstSegment)); GST_DEBUG_OBJECT (typefind, "configuring seek"); gst_segment_do_seek (&seeksegment, rate, format, flags, start_type, start, stop_type, stop, NULL); flush = ! !(flags & GST_SEEK_FLAG_FLUSH); GST_DEBUG_OBJECT (typefind, "New segment %" GST_SEGMENT_FORMAT, &seeksegment); if (flush) { GST_DEBUG_OBJECT (typefind, "Starting flush"); gst_pad_push_event (typefind->sink, gst_event_new_flush_start ()); gst_pad_push_event (typefind->src, gst_event_new_flush_start ()); } else { GST_DEBUG_OBJECT (typefind, "Non-flushing seek, pausing task"); gst_pad_pause_task (typefind->sink); } /* now grab the stream lock so that streaming cannot continue, for * non flushing seeks when the element is in PAUSED this could block * forever. */ GST_DEBUG_OBJECT (typefind, "Waiting for streaming to stop"); GST_PAD_STREAM_LOCK (typefind->sink); if (flush) { GST_DEBUG_OBJECT (typefind, "Stopping flush"); gst_pad_push_event (typefind->sink, gst_event_new_flush_stop (TRUE)); gst_pad_push_event (typefind->src, gst_event_new_flush_stop (TRUE)); } /* now update the real segment info */ GST_DEBUG_OBJECT (typefind, "Committing new seek segment"); memcpy (&typefind->segment, &seeksegment, sizeof (GstSegment)); typefind->offset = typefind->segment.start; /* notify start of new segment */ if (typefind->segment.flags & GST_SEGMENT_FLAG_SEGMENT) { GstMessage *msg; msg = gst_message_new_segment_start (GST_OBJECT (typefind), GST_FORMAT_BYTES, typefind->segment.start); gst_element_post_message (GST_ELEMENT (typefind), msg); } typefind->need_segment = TRUE; /* restart our task since it might have been stopped when we did the * flush. */ gst_pad_start_task (typefind->sink, (GstTaskFunction) gst_type_find_element_loop, typefind->sink, NULL); /* streaming can continue now */ GST_PAD_STREAM_UNLOCK (typefind->sink); return TRUE; }
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; #ifdef HAVE_WILDMIDI_0_2_2 gboolean accurate; #endif 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); #ifdef HAVE_WILDMIDI_0_2_2 accurate = ((flags & GST_SEEK_FLAG_ACCURATE) == GST_SEEK_FLAG_ACCURATE); #endif 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 (TRUE)); } /* update the segment now */ gst_segment_do_seek (segment, rate, dst_format, flags, start_type, start, stop_type, stop, &update); /* we need to seek to position in the segment now, sample will be updated */ sample = segment->position; GST_OBJECT_LOCK (wildmidi); #ifdef HAVE_WILDMIDI_0_2_2 if (accurate) { WildMidi_SampledSeek (wildmidi->song, &sample); } else { WildMidi_FastSeek (wildmidi->song, &sample); } #else WildMidi_FastSeek (wildmidi->song, &sample); #endif GST_OBJECT_UNLOCK (wildmidi); segment->start = segment->time = segment->position = 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, NULL); wildmidi->discont = TRUE; GST_PAD_STREAM_UNLOCK (wildmidi->sinkpad); GST_DEBUG ("seek done"); return TRUE; }
static gboolean gst_hls_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek) { GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux); GstFormat format; GstSeekFlags flags; GstSeekType start_type, stop_type; gint64 start, stop; gdouble rate; GList *walk, *current_file = NULL; GstClockTime current_pos, target_pos; gint64 current_sequence; GstM3U8MediaFile *file; guint64 bitrate; gboolean snap_before, snap_after, snap_nearest, keyunit; gboolean reverse; gst_event_parse_seek (seek, &rate, &format, &flags, &start_type, &start, &stop_type, &stop); bitrate = gst_hls_demux_get_bitrate (hlsdemux); /* properly cleanup pending decryption status */ if (flags & GST_SEEK_FLAG_FLUSH) { gst_hls_demux_clear_pending_data (hlsdemux); } /* Use I-frame variants for trick modes */ if (hlsdemux->client->main->iframe_lists && rate < -1.0 && demux->segment.rate >= -1.0 && demux->segment.rate <= 1.0) { GError *err = NULL; GST_M3U8_CLIENT_LOCK (hlsdemux->client); /* Switch to I-frame variant */ hlsdemux->client->main->current_variant = hlsdemux->client->main->iframe_lists; GST_M3U8_CLIENT_UNLOCK (hlsdemux->client); gst_m3u8_client_set_current (hlsdemux->client, hlsdemux->client->main->iframe_lists->data); gst_uri_downloader_reset (demux->downloader); if (!gst_hls_demux_update_playlist (hlsdemux, FALSE, &err)) { GST_ELEMENT_ERROR_FROM_ERROR (hlsdemux, "Could not switch playlist", err); return FALSE; } //hlsdemux->discont = TRUE; hlsdemux->do_typefind = TRUE; gst_hls_demux_change_playlist (hlsdemux, bitrate / ABS (rate), NULL); } else if (rate > -1.0 && rate <= 1.0 && (demux->segment.rate < -1.0 || demux->segment.rate > 1.0)) { GError *err = NULL; GST_M3U8_CLIENT_LOCK (hlsdemux->client); /* Switch to normal variant */ hlsdemux->client->main->current_variant = hlsdemux->client->main->lists; GST_M3U8_CLIENT_UNLOCK (hlsdemux->client); gst_m3u8_client_set_current (hlsdemux->client, hlsdemux->client->main->lists->data); gst_uri_downloader_reset (demux->downloader); if (!gst_hls_demux_update_playlist (hlsdemux, FALSE, &err)) { GST_ELEMENT_ERROR_FROM_ERROR (hlsdemux, "Could not switch playlist", err); return FALSE; } //hlsdemux->discont = TRUE; hlsdemux->do_typefind = TRUE; /* TODO why not continue using the same? that was being used up to now? */ gst_hls_demux_change_playlist (hlsdemux, bitrate, NULL); } GST_M3U8_CLIENT_LOCK (hlsdemux->client); file = GST_M3U8_MEDIA_FILE (hlsdemux->client->current->files->data); current_sequence = file->sequence; current_pos = 0; reverse = rate < 0; target_pos = reverse ? stop : start; /* Snap to segment boundary. Improves seek performance on slow machines. */ keyunit = ! !(flags & GST_SEEK_FLAG_KEY_UNIT); snap_nearest = (flags & GST_SEEK_FLAG_SNAP_NEAREST) == GST_SEEK_FLAG_SNAP_NEAREST; snap_before = ! !(flags & GST_SEEK_FLAG_SNAP_BEFORE); snap_after = ! !(flags & GST_SEEK_FLAG_SNAP_AFTER); /* FIXME: Here we need proper discont handling */ for (walk = hlsdemux->client->current->files; walk; walk = walk->next) { file = walk->data; current_sequence = file->sequence; current_file = walk; if ((!reverse && snap_after) || snap_nearest) { if (current_pos >= target_pos) break; if (snap_nearest && target_pos - current_pos < file->duration / 2) break; } else if (reverse && snap_after) { /* check if the next fragment is our target, in this case we want to * start from the previous fragment */ GstClockTime next_pos = current_pos + file->duration; if (next_pos <= target_pos && target_pos < next_pos + file->duration) { break; } } else if (current_pos <= target_pos && target_pos < current_pos + file->duration) { break; } current_pos += file->duration; } if (walk == NULL) { GST_DEBUG_OBJECT (demux, "seeking further than track duration"); current_sequence++; } GST_DEBUG_OBJECT (demux, "seeking to sequence %u", (guint) current_sequence); hlsdemux->reset_pts = TRUE; hlsdemux->client->sequence = current_sequence; hlsdemux->client->current_file = current_file ? current_file : hlsdemux->client->current->files; hlsdemux->client->sequence_position = current_pos; GST_M3U8_CLIENT_UNLOCK (hlsdemux->client); /* Play from the end of the current selected segment */ if (reverse && (snap_before || snap_after || snap_nearest)) current_pos += file->duration; if (keyunit || snap_before || snap_after || snap_nearest) { if (!reverse) gst_segment_do_seek (&demux->segment, rate, format, flags, start_type, current_pos, stop_type, stop, NULL); else gst_segment_do_seek (&demux->segment, rate, format, flags, start_type, start, stop_type, current_pos, NULL); } return TRUE; }
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_do_seek (&seeksegment, rate, format, flags, start_type, start, stop_type, stop, NULL); } /* get the desired position */ last_stop = seeksegment.position; 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 (TRUE)); gst_pad_push_event (rp->srcpad, gst_event_new_flush_stop (TRUE)); } 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.position)); } /* 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); rp->start_segment = gst_event_new_segment (&rp->segment); } rp->discont = TRUE; GST_LOG_OBJECT (rp, "start streaming"); gst_pad_start_task (rp->sinkpad, (GstTaskFunction) gst_raw_parse_loop, rp, NULL); 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_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); gst_segment_do_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 (TRUE)); gst_pad_push_event (demux->srcpad, gst_event_new_flush_stop (TRUE)); 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.position)); } 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, NULL); /* 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_image_freeze_src_event (GstPad * pad, GstObject * parent, GstEvent * event) { GstImageFreeze *self = GST_IMAGE_FREEZE (parent); gboolean ret; GST_LOG_OBJECT (pad, "Got %s event", GST_EVENT_TYPE_NAME (event)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_NAVIGATION: case GST_EVENT_QOS: case GST_EVENT_LATENCY: case GST_EVENT_STEP: GST_DEBUG_OBJECT (pad, "Dropping event"); gst_event_unref (event); ret = TRUE; break; case GST_EVENT_SEEK: { gdouble rate; GstFormat format; GstSeekFlags flags; GstSeekType start_type, stop_type; gint64 start, stop; gint64 last_stop; gboolean start_task; gboolean flush; gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start, &stop_type, &stop); gst_event_unref (event); flush = ! !(flags & GST_SEEK_FLAG_FLUSH); if (format != GST_FORMAT_TIME && format != GST_FORMAT_DEFAULT) { GST_ERROR_OBJECT (pad, "Seek in invalid format: %s", gst_format_get_name (format)); ret = FALSE; break; } if (format == GST_FORMAT_DEFAULT) { format = GST_FORMAT_TIME; if (!gst_image_freeze_convert (self, GST_FORMAT_DEFAULT, start, &format, &start) || !gst_image_freeze_convert (self, GST_FORMAT_DEFAULT, stop, &format, &stop) || start == -1 || stop == -1) { GST_ERROR_OBJECT (pad, "Failed to convert seek from DEFAULT format into TIME format"); ret = FALSE; break; } } if (flush) { GstEvent *e; g_atomic_int_set (&self->seeking, 1); e = gst_event_new_flush_start (); gst_pad_push_event (self->srcpad, e); } else { gst_pad_pause_task (self->srcpad); } GST_PAD_STREAM_LOCK (self->srcpad); g_mutex_lock (&self->lock); gst_segment_do_seek (&self->segment, rate, format, flags, start_type, start, stop_type, stop, NULL); self->need_segment = TRUE; last_stop = self->segment.position; start_task = self->buffer != NULL; g_mutex_unlock (&self->lock); if (flush) { GstEvent *e; e = gst_event_new_flush_stop (TRUE); gst_pad_push_event (self->srcpad, e); g_atomic_int_set (&self->seeking, 0); } if (flags & GST_SEEK_FLAG_SEGMENT) { GstMessage *m; m = gst_message_new_segment_start (GST_OBJECT (self), format, last_stop); gst_element_post_message (GST_ELEMENT (self), m); } GST_PAD_STREAM_UNLOCK (self->srcpad); GST_DEBUG_OBJECT (pad, "Seek successful"); if (start_task) { g_mutex_lock (&self->lock); if (self->buffer != NULL) gst_pad_start_task (self->srcpad, (GstTaskFunction) gst_image_freeze_src_loop, self->srcpad, NULL); g_mutex_unlock (&self->lock); } ret = TRUE; break; } case GST_EVENT_FLUSH_START: gst_image_freeze_reset (self); /* fall through */ default: ret = gst_pad_push_event (self->sinkpad, event); break; } return ret; }
static gboolean gst_midi_parse_perform_seek (GstMidiParse * midiparse, GstEvent * event) { gboolean res = TRUE, tres; gdouble rate; GstFormat seek_format; GstSeekFlags flags; GstSeekType start_type, stop_type; gint64 start, stop; gboolean flush; gboolean update; GstSegment seeksegment; guint32 seqnum; GstEvent *tevent; GST_DEBUG_OBJECT (midiparse, "doing seek: %" GST_PTR_FORMAT, event); if (event) { gst_event_parse_seek (event, &rate, &seek_format, &flags, &start_type, &start, &stop_type, &stop); if (seek_format != GST_FORMAT_TIME) goto invalid_format; flush = flags & GST_SEEK_FLAG_FLUSH; seqnum = gst_event_get_seqnum (event); } else { flush = FALSE; /* get next seqnum */ seqnum = gst_util_seqnum_next (); } /* send flush start */ if (flush) { tevent = gst_event_new_flush_start (); gst_event_set_seqnum (tevent, seqnum); gst_pad_push_event (midiparse->srcpad, tevent); } else gst_pad_pause_task (midiparse->srcpad); /* grab streaming lock, this should eventually be possible, either * because the task is paused, our streaming thread stopped * or because our peer is flushing. */ GST_PAD_STREAM_LOCK (midiparse->sinkpad); if (G_UNLIKELY (midiparse->seqnum == seqnum)) { /* we have seen this event before, issue a warning for now */ GST_WARNING_OBJECT (midiparse, "duplicate event found %" G_GUINT32_FORMAT, seqnum); } else { midiparse->seqnum = seqnum; GST_DEBUG_OBJECT (midiparse, "seek with seqnum %" G_GUINT32_FORMAT, seqnum); } /* Copy the current segment info into the temp segment that we can actually * attempt the seek with. We only update the real segment if the seek succeeds. */ memcpy (&seeksegment, &midiparse->segment, sizeof (GstSegment)); /* now configure the final seek segment */ if (event) { gst_segment_do_seek (&seeksegment, rate, seek_format, flags, start_type, start, stop_type, stop, &update); } /* Else, no seek event passed, so we're just (re)starting the current segment. */ GST_DEBUG_OBJECT (midiparse, "segment configured from %" G_GINT64_FORMAT " to %" G_GINT64_FORMAT ", position %" G_GINT64_FORMAT, seeksegment.start, seeksegment.stop, seeksegment.position); /* do the seek, segment.position contains the new position. */ res = gst_midi_parse_do_seek (midiparse, &seeksegment); /* and prepare to continue streaming */ if (flush) { tevent = gst_event_new_flush_stop (TRUE); gst_event_set_seqnum (tevent, seqnum); /* send flush stop, peer will accept data and events again. We * are not yet providing data as we still have the STREAM_LOCK. */ gst_pad_push_event (midiparse->srcpad, tevent); } /* if the seek was successful, we update our real segment and push * out the new segment. */ if (res) { GST_OBJECT_LOCK (midiparse); memcpy (&midiparse->segment, &seeksegment, sizeof (GstSegment)); GST_OBJECT_UNLOCK (midiparse); if (seeksegment.flags & GST_SEGMENT_FLAG_SEGMENT) { GstMessage *message; message = gst_message_new_segment_start (GST_OBJECT (midiparse), seeksegment.format, seeksegment.position); gst_message_set_seqnum (message, seqnum); gst_element_post_message (GST_ELEMENT (midiparse), message); } /* 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 = seeksegment.stop) == -1) stop = seeksegment.duration; midiparse->segment_pending = TRUE; midiparse->discont = TRUE; } /* and restart the task in case it got paused explicitly or by * the FLUSH_START event we pushed out. */ tres = gst_pad_start_task (midiparse->sinkpad, (GstTaskFunction) gst_midi_parse_loop, midiparse->sinkpad, NULL); if (res && !tres) res = FALSE; /* and release the lock again so we can continue streaming */ GST_PAD_STREAM_UNLOCK (midiparse->sinkpad); return res; /* ERROR */ invalid_format: { GST_DEBUG_OBJECT (midiparse, "Unsupported seek format %s", gst_format_get_name (seek_format)); return FALSE; } }
static gboolean gst_adder_src_event (GstPad * pad, GstObject * parent, GstEvent * event) { GstAdder *adder; gboolean result; adder = GST_ADDER (parent); GST_DEBUG_OBJECT (pad, "Got %s event on src pad", GST_EVENT_TYPE_NAME (event)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEEK: { GstSeekFlags flags; gdouble rate; GstSeekType start_type, stop_type; gint64 start, stop; GstFormat seek_format, dest_format; gboolean flush; /* parse the seek parameters */ gst_event_parse_seek (event, &rate, &seek_format, &flags, &start_type, &start, &stop_type, &stop); if ((start_type != GST_SEEK_TYPE_NONE) && (start_type != GST_SEEK_TYPE_SET)) { result = FALSE; GST_DEBUG_OBJECT (adder, "seeking failed, unhandled seek type for start: %d", start_type); goto done; } if ((stop_type != GST_SEEK_TYPE_NONE) && (stop_type != GST_SEEK_TYPE_SET)) { result = FALSE; GST_DEBUG_OBJECT (adder, "seeking failed, unhandled seek type for end: %d", stop_type); goto done; } dest_format = adder->segment.format; if (seek_format != dest_format) { result = FALSE; GST_DEBUG_OBJECT (adder, "seeking failed, unhandled seek format: %d", seek_format); goto done; } flush = (flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH; /* check if we are flushing */ if (flush) { /* flushing seek, start flush downstream, the flush will be done * when all pads received a FLUSH_STOP. * Make sure we accept nothing anymore and return WRONG_STATE. * We send a flush-start before, to ensure no streaming is done * as we need to take the stream lock. */ gst_pad_push_event (adder->srcpad, gst_event_new_flush_start ()); gst_collect_pads_set_flushing (adder->collect, TRUE); /* We can't send FLUSH_STOP here since upstream could start pushing data * after we unlock adder->collect. * We set flush_stop_pending to TRUE instead and send FLUSH_STOP after * forwarding the seek upstream or from gst_adder_collected, * whichever happens first. */ GST_COLLECT_PADS_STREAM_LOCK (adder->collect); adder->flush_stop_pending = TRUE; GST_COLLECT_PADS_STREAM_UNLOCK (adder->collect); GST_DEBUG_OBJECT (adder, "mark pending flush stop event"); } GST_DEBUG_OBJECT (adder, "handling seek event: %" GST_PTR_FORMAT, event); /* now wait for the collected to be finished and mark a new * segment. After we have the lock, no collect function is running and no * new collect function will be called for as long as we're flushing. */ GST_COLLECT_PADS_STREAM_LOCK (adder->collect); /* clip position and update our segment */ if (adder->segment.stop != -1) { adder->segment.position = adder->segment.stop; } gst_segment_do_seek (&adder->segment, rate, seek_format, flags, start_type, start, stop_type, stop, NULL); if (flush) { /* Yes, we need to call _set_flushing again *WHEN* the streaming threads * have stopped so that the cookie gets properly updated. */ gst_collect_pads_set_flushing (adder->collect, TRUE); } GST_COLLECT_PADS_STREAM_UNLOCK (adder->collect); GST_DEBUG_OBJECT (adder, "forwarding seek event: %" GST_PTR_FORMAT, event); GST_DEBUG_OBJECT (adder, "updated segment: %" GST_SEGMENT_FORMAT, &adder->segment); /* we're forwarding seek to all upstream peers and wait for one to reply * with a newsegment-event before we send a newsegment-event downstream */ g_atomic_int_set (&adder->new_segment_pending, TRUE); result = forward_event (adder, event, flush); if (!result) { /* seek failed. maybe source is a live source. */ GST_DEBUG_OBJECT (adder, "seeking failed"); } if (g_atomic_int_compare_and_exchange (&adder->flush_stop_pending, TRUE, FALSE)) { GST_DEBUG_OBJECT (adder, "pending flush stop"); if (!gst_pad_push_event (adder->srcpad, gst_event_new_flush_stop (TRUE))) { GST_WARNING_OBJECT (adder, "Sending flush stop event failed"); } } break; } case GST_EVENT_QOS: /* QoS might be tricky */ result = FALSE; gst_event_unref (event); break; case GST_EVENT_NAVIGATION: /* navigation is rather pointless. */ result = FALSE; gst_event_unref (event); break; default: /* just forward the rest for now */ GST_DEBUG_OBJECT (adder, "forward unhandled event: %s", GST_EVENT_TYPE_NAME (event)); result = forward_event (adder, event, FALSE); break; } done: return result; }