/* return TRUE to discard the Segment */ static gboolean compare_segments (CollectStructure * collect, Segment * segment, GstEvent * event) { const GstSegment *received_segment; guint64 running_stop, running_start, running_duration; gst_event_parse_segment (event, &received_segment); GST_DEBUG ("Got Segment rate:%f, format:%s, start:%" GST_TIME_FORMAT ", stop:%" GST_TIME_FORMAT ", time:%" GST_TIME_FORMAT ", base:%" GST_TIME_FORMAT ", offset:%" GST_TIME_FORMAT, received_segment->rate, gst_format_get_name (received_segment->format), GST_TIME_ARGS (received_segment->start), GST_TIME_ARGS (received_segment->stop), GST_TIME_ARGS (received_segment->time), GST_TIME_ARGS (received_segment->base), GST_TIME_ARGS (received_segment->offset)); GST_DEBUG ("[RUNNING] start:%" GST_TIME_FORMAT " [STREAM] start:%" GST_TIME_FORMAT, GST_TIME_ARGS (gst_segment_to_running_time (received_segment, GST_FORMAT_TIME, received_segment->start)), GST_TIME_ARGS (gst_segment_to_stream_time (received_segment, GST_FORMAT_TIME, received_segment->start))); GST_DEBUG ("Expecting rate:%f, format:%s, start:%" GST_TIME_FORMAT ", stop:%" GST_TIME_FORMAT ", position:%" GST_TIME_FORMAT ", base:%" GST_TIME_FORMAT, segment->rate, gst_format_get_name (segment->format), GST_TIME_ARGS (segment->start), GST_TIME_ARGS (segment->stop), GST_TIME_ARGS (segment->position), GST_TIME_ARGS (collect->expected_base)); running_start = gst_segment_to_running_time (received_segment, GST_FORMAT_TIME, received_segment->start); running_stop = gst_segment_to_running_time (received_segment, GST_FORMAT_TIME, received_segment->stop); running_duration = running_stop - running_start; fail_if (received_segment->rate != segment->rate); fail_if (received_segment->format != segment->format); fail_unless_equals_int64 (received_segment->time, segment->position); fail_unless_equals_int64 (received_segment->base, collect->expected_base); fail_unless_equals_uint64 (received_segment->stop - received_segment->start, segment->stop - segment->start); collect->expected_base += running_duration; GST_DEBUG ("Segment was valid, discarding expected Segment"); return TRUE; }
static gboolean event_equals_newsegment (GstEvent * event, gboolean update, gdouble rate, GstFormat format, gint64 start, gint64 stop, gint64 position) { gboolean ns_update; gdouble ns_rate; GstFormat ns_format; gint64 ns_start; gint64 ns_stop; gint64 ns_position; if (GST_EVENT_TYPE (event) != GST_EVENT_NEWSEGMENT) { return FALSE; } gst_event_parse_new_segment (event, &ns_update, &ns_rate, &ns_format, &ns_start, &ns_stop, &ns_position); GST_DEBUG ("update %d, rate %lf, format %s, start %" GST_TIME_FORMAT ", stop %" GST_TIME_FORMAT ", position %" GST_TIME_FORMAT, ns_update, ns_rate, gst_format_get_name (ns_format), GST_TIME_ARGS (ns_start), GST_TIME_ARGS (ns_stop), GST_TIME_ARGS (ns_position)); return (ns_update == update && ns_rate == rate && ns_format == format && ns_start == start && ns_stop == stop && ns_position == position); }
static gboolean gst_gio_base_sink_event (GstBaseSink * base_sink, GstEvent * event) { GstGioBaseSink *sink = GST_GIO_BASE_SINK (base_sink); GstFlowReturn ret = GST_FLOW_OK; if (sink->stream == NULL) return TRUE; switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEGMENT: if (G_IS_OUTPUT_STREAM (sink->stream)) { const GstSegment *segment; gst_event_parse_segment (event, &segment); if (segment->format != GST_FORMAT_BYTES) { GST_WARNING_OBJECT (sink, "ignored SEGMENT event in %s format", gst_format_get_name (segment->format)); break; } if (GST_GIO_STREAM_IS_SEEKABLE (sink->stream)) { ret = gst_gio_seek (sink, G_SEEKABLE (sink->stream), segment->start, sink->cancel); if (ret == GST_FLOW_OK) sink->position = segment->start; } else { ret = GST_FLOW_NOT_SUPPORTED; } } break; case GST_EVENT_EOS: case GST_EVENT_FLUSH_START: if (G_IS_OUTPUT_STREAM (sink->stream)) { gboolean success; GError *err = NULL; success = g_output_stream_flush (sink->stream, sink->cancel, &err); if (!success && !gst_gio_error (sink, "g_output_stream_flush", &err, &ret)) { GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, (NULL), ("flush failed: %s", err->message)); g_clear_error (&err); } } break; default: break; } if (ret == GST_FLOW_OK) return GST_BASE_SINK_CLASS (parent_class)->event (base_sink, event); else { gst_event_unref (event); return FALSE; } }
static gboolean gst_audio_aggregator_sink_event (GstAggregator * agg, GstAggregatorPad * aggpad, GstEvent * event) { gboolean res = TRUE; GST_DEBUG_OBJECT (aggpad, "Got %s event on sink pad", GST_EVENT_TYPE_NAME (event)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEGMENT: { const GstSegment *segment; gst_event_parse_segment (event, &segment); if (segment->format != GST_FORMAT_TIME) { GST_ERROR_OBJECT (agg, "Segment of type %s are not supported," " only TIME segments are supported", gst_format_get_name (segment->format)); gst_event_unref (event); event = NULL; res = FALSE; break; } GST_OBJECT_LOCK (agg); if (segment->rate != agg->segment.rate) { GST_ERROR_OBJECT (aggpad, "Got segment event with wrong rate %lf, expected %lf", segment->rate, agg->segment.rate); res = FALSE; gst_event_unref (event); event = NULL; } else if (segment->rate < 0.0) { GST_ERROR_OBJECT (aggpad, "Negative rates not supported yet"); res = FALSE; gst_event_unref (event); event = NULL; } else { GstAudioAggregatorPad *pad = GST_AUDIO_AGGREGATOR_PAD (aggpad); GST_OBJECT_LOCK (pad); pad->priv->new_segment = TRUE; GST_OBJECT_UNLOCK (pad); } GST_OBJECT_UNLOCK (agg); break; } default: break; } if (event != NULL) return GST_AGGREGATOR_CLASS (gst_audio_aggregator_parent_class)->sink_event (agg, aggpad, event); return res; }
static double em_pos_get(void *video) { Emotion_Gstreamer_Video *ev; GstFormat fmt; gint64 val; gboolean ret; ev = video; fmt = GST_FORMAT_TIME; if (!ev->pipeline) return 0.0; ret = gst_element_query_position(ev->pipeline, &fmt, &val); if (!ret) return ev->position; if (fmt != GST_FORMAT_TIME) { ERR("requrested position in time, but got %s instead.", gst_format_get_name(fmt)); return ev->position; } ev->position = val / 1000000000.0; return ev->position; }
/* will be called in push mode */ static gboolean gst_app_src_do_seek (GstBaseSrc * src, GstSegment * segment) { GstAppSrc *appsrc = GST_APP_SRC (src); gint64 desired_position; gboolean res = FALSE; desired_position = segment->last_stop; GST_DEBUG_OBJECT (appsrc, "seeking to %" G_GINT64_FORMAT ", format %s", desired_position, gst_format_get_name (segment->format)); /* no need to try to seek in streaming mode */ if (appsrc->stream_type == GST_APP_STREAM_TYPE_STREAM) return TRUE; g_signal_emit (appsrc, gst_app_src_signals[SIGNAL_SEEK_DATA], 0, desired_position, &res); if (res) { GST_DEBUG_OBJECT (appsrc, "flushing queue"); gst_app_src_flush_queued (appsrc); } else { GST_WARNING_OBJECT (appsrc, "seek failed"); } return res; }
static gboolean gst_pngdec_sink_event (GstPad * pad, GstEvent * event) { GstPngDec *pngdec; gboolean res; pngdec = GST_PNGDEC (gst_pad_get_parent (pad)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_NEWSEGMENT:{ gdouble rate, arate; gboolean update; gint64 start, stop, position; GstFormat fmt; gst_event_parse_new_segment_full (event, &update, &rate, &arate, &fmt, &start, &stop, &position); gst_segment_set_newsegment_full (&pngdec->segment, update, rate, arate, fmt, start, stop, position); GST_LOG_OBJECT (pngdec, "NEWSEGMENT (%s)", gst_format_get_name (fmt)); if (fmt == GST_FORMAT_TIME) { pngdec->need_newsegment = FALSE; res = gst_pad_push_event (pngdec->srcpad, event); } else { gst_event_unref (event); res = TRUE; } break; } case GST_EVENT_FLUSH_STOP: { gst_pngdec_libpng_clear (pngdec); gst_pngdec_libpng_init (pngdec); png_set_progressive_read_fn (pngdec->png, pngdec, user_info_callback, user_endrow_callback, user_end_callback); pngdec->ret = GST_FLOW_OK; gst_segment_init (&pngdec->segment, GST_FORMAT_UNDEFINED); res = gst_pad_push_event (pngdec->srcpad, event); break; } case GST_EVENT_EOS: { GST_LOG_OBJECT (pngdec, "EOS"); gst_pngdec_libpng_clear (pngdec); pngdec->ret = GST_FLOW_UNEXPECTED; res = gst_pad_push_event (pngdec->srcpad, event); break; } default: res = gst_pad_push_event (pngdec->srcpad, event); break; } gst_object_unref (pngdec); return res; }
static gboolean gst_fd_sink_event (GstBaseSink * sink, GstEvent * event) { GstEventType type; GstFdSink *fdsink; fdsink = GST_FD_SINK (sink); type = GST_EVENT_TYPE (event); switch (type) { case GST_EVENT_SEGMENT: { const GstSegment *segment; gst_event_parse_segment (event, &segment); if (segment->format == GST_FORMAT_BYTES) { /* only try to seek and fail when we are going to a different * position */ if (fdsink->current_pos != segment->start) { /* FIXME, the seek should be performed on the pos field, start/stop are * just boundaries for valid bytes offsets. We should also fill the file * with zeroes if the new position extends the current EOF (sparse streams * and segment accumulation). */ if (!gst_fd_sink_do_seek (fdsink, (guint64) segment->start)) goto seek_failed; } } else { GST_DEBUG_OBJECT (fdsink, "Ignored SEGMENT event of format %u (%s)", (guint) segment->format, gst_format_get_name (segment->format)); } break; } default: break; } return GST_BASE_SINK_CLASS (parent_class)->event (sink, event); seek_failed: { GST_ELEMENT_ERROR (fdsink, RESOURCE, SEEK, (NULL), ("Error while seeking on file descriptor %d: %s", fdsink->fd, g_strerror (errno))); gst_event_unref (event); return FALSE; } }
static gboolean gst_gnome_vfs_sink_handle_event (GstBaseSink * basesink, GstEvent * event) { GstGnomeVFSSink *sink; gboolean ret = TRUE; sink = GST_GNOME_VFS_SINK (basesink); GST_DEBUG_OBJECT (sink, "processing %s event", GST_EVENT_TYPE_NAME (event)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_NEWSEGMENT: { GnomeVFSResult res; GstFormat format; gint64 offset; gst_event_parse_new_segment (event, NULL, NULL, &format, &offset, NULL, NULL); if (format != GST_FORMAT_BYTES) { GST_WARNING_OBJECT (sink, "ignored NEWSEGMENT event in %s format", gst_format_get_name (format)); break; } GST_LOG_OBJECT (sink, "seeking to offset %" G_GINT64_FORMAT, offset); res = gnome_vfs_seek (sink->handle, GNOME_VFS_SEEK_START, offset); if (res != GNOME_VFS_OK) { GST_ERROR_OBJECT (sink, "Failed to seek to offset %" G_GINT64_FORMAT ": %s", offset, gnome_vfs_result_to_string (res)); ret = FALSE; } else { sink->current_pos = offset; } break; } case GST_EVENT_FLUSH_START: case GST_EVENT_EOS: { /* No need to flush with GnomeVfs */ break; } default: break; } return ret; }
static double em_len_get(void *video) { Emotion_Gstreamer_Video *ev; Emotion_Video_Stream *vstream; Emotion_Audio_Stream *astream; Eina_List *l; GstFormat fmt; gint64 val; gboolean ret; ev = video; fmt = GST_FORMAT_TIME; if (!ev->pipeline) return 0.0; ret = gst_element_query_duration(ev->pipeline, &fmt, &val); if (!ret) goto fallback; if (fmt != GST_FORMAT_TIME) { DBG("requrested duration in time, but got %s instead.", gst_format_get_name(fmt)); goto fallback; } if (val <= 0.0) goto fallback; return val / 1000000000.0; fallback: if (!_emotion_gstreamer_video_pipeline_parse(ev, EINA_FALSE)) return 0.0; EINA_LIST_FOREACH(ev->audio_streams, l, astream) if (astream->length_time >= 0) return astream->length_time; EINA_LIST_FOREACH(ev->video_streams, l, vstream) if (vstream->length_time >= 0) return vstream->length_time; return 0.0; }
/** * gst_event_new_new_segment_full: * @update: Whether this segment is an update to a previous one * @rate: A new rate for playback * @applied_rate: The rate factor which has already been applied * @format: The format of the segment values * @start: The start value of the segment * @stop: The stop value of the segment * @position: stream position * * Allocate a new newsegment event with the given format/values triplets. * * The newsegment event marks the range of buffers to be processed. All * data not within the segment range is not to be processed. This can be * used intelligently by plugins to apply more efficient methods of skipping * unneeded data. The valid range is expressed with the @start and @stop * values. * * The position value of the segment is used in conjunction with the start * value to convert the buffer timestamps into the stream time. This is * usually done in sinks to report the current stream_time. * @position represents the stream_time of a buffer carrying a timestamp of * @start. @position cannot be -1. * * @start cannot be -1, @stop can be -1. If there * is a valid @stop given, it must be greater or equal the @start, including * when the indicated playback @rate is < 0. * * The @applied_rate value provides information about any rate adjustment that * has already been made to the timestamps and content on the buffers of the * stream. (@rate * @applied_rate) should always equal the rate that has been * requested for playback. For example, if an element has an input segment * with intended playback @rate of 2.0 and applied_rate of 1.0, it can adjust * incoming timestamps and buffer content by half and output a newsegment event * with @rate of 1.0 and @applied_rate of 2.0 * * After a newsegment event, the buffer stream time is calculated with: * * position + (TIMESTAMP(buf) - start) * ABS (rate * applied_rate) * * Returns: (transfer full): a new newsegment event. * * Since: 0.10.6 */ GstEvent * gst_event_new_new_segment_full (gboolean update, gdouble rate, gdouble applied_rate, GstFormat format, gint64 start, gint64 stop, gint64 position) { GstEvent *event; GstStructure *structure; g_return_val_if_fail (rate != 0.0, NULL); g_return_val_if_fail (applied_rate != 0.0, NULL); if (format == GST_FORMAT_TIME) { GST_CAT_INFO (GST_CAT_EVENT, "creating newsegment update %d, rate %lf, format GST_FORMAT_TIME, " "start %" GST_TIME_FORMAT ", stop %" GST_TIME_FORMAT ", position %" GST_TIME_FORMAT, update, rate, GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (position)); } else { GST_CAT_INFO (GST_CAT_EVENT, "creating newsegment update %d, rate %lf, format %s, " "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT ", position %" G_GINT64_FORMAT, update, rate, gst_format_get_name (format), start, stop, position); } g_return_val_if_fail (position != -1, NULL); g_return_val_if_fail (start != -1, NULL); if (stop != -1) g_return_val_if_fail (start <= stop, NULL); structure = gst_structure_id_new (GST_QUARK (EVENT_NEWSEGMENT), GST_QUARK (UPDATE), G_TYPE_BOOLEAN, update, GST_QUARK (RATE), G_TYPE_DOUBLE, rate, GST_QUARK (APPLIED_RATE), G_TYPE_DOUBLE, applied_rate, GST_QUARK (FORMAT), GST_TYPE_FORMAT, format, GST_QUARK (START), G_TYPE_INT64, start, GST_QUARK (STOP), G_TYPE_INT64, stop, GST_QUARK (POSITION), G_TYPE_INT64, position, NULL); event = gst_event_new_custom (GST_EVENT_NEWSEGMENT, structure); return event; }
bool ofxGstRTPServer::on_message(GstMessage * msg){ // read messages from the pipeline like dropped packages switch (GST_MESSAGE_TYPE (msg)) { case GST_MESSAGE_ELEMENT:{ GstObject * messageSrc = GST_MESSAGE_SRC(msg); ofLogVerbose(LOG_NAME) << "Got " << GST_MESSAGE_TYPE_NAME(msg) << " message from " << GST_MESSAGE_SRC_NAME(msg); ofLogVerbose(LOG_NAME) << "Message source type: " << G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(messageSrc)); ofLogVerbose(LOG_NAME) << "With structure name: " << gst_structure_get_name(gst_message_get_structure(msg)); ofLogVerbose(LOG_NAME) << gst_structure_to_string(gst_message_get_structure(msg)); return true; } case GST_MESSAGE_QOS:{ GstObject * messageSrc = GST_MESSAGE_SRC(msg); ofLogVerbose(LOG_NAME) << "Got " << GST_MESSAGE_TYPE_NAME(msg) << " message from " << GST_MESSAGE_SRC_NAME(msg); ofLogVerbose(LOG_NAME) << "Message source type: " << G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(messageSrc)); GstFormat format; guint64 processed; guint64 dropped; gst_message_parse_qos_stats(msg,&format,&processed,&dropped); ofLogVerbose(LOG_NAME) << "format " << gst_format_get_name(format) << " processed " << processed << " dropped " << dropped; gint64 jitter; gdouble proportion; gint quality; gst_message_parse_qos_values(msg,&jitter,&proportion,&quality); ofLogVerbose(LOG_NAME) << "jitter " << jitter << " proportion " << proportion << " quality " << quality; gboolean live; guint64 running_time; guint64 stream_time; guint64 timestamp; guint64 duration; gst_message_parse_qos(msg,&live,&running_time,&stream_time,×tamp,&duration); ofLogVerbose(LOG_NAME) << "live stream " << live << " runninng_time " << running_time << " stream_time " << stream_time << " timestamp " << timestamp << " duration " << duration; return true; } default: //ofLogVerbose(LOG_NAME) << "Got " << GST_MESSAGE_TYPE_NAME(msg) << " message from " << GST_MESSAGE_SRC_NAME(msg); return false; } }
static gboolean gst_segment_clip_event (GstPad * pad, GstEvent * event) { GstSegmentClip *self = GST_SEGMENT_CLIP (gst_pad_get_parent (pad)); GstPad *otherpad; gboolean ret; GST_LOG_OBJECT (pad, "Got %s event", GST_EVENT_TYPE_NAME (event)); otherpad = (pad == self->srcpad) ? self->sinkpad : self->srcpad; ret = gst_pad_push_event (otherpad, event); if (ret) { switch (GST_EVENT_TYPE (event)) { case GST_EVENT_NEWSEGMENT:{ GstFormat fmt; gboolean is_update; gint64 start, end, base; gdouble rate; gst_event_parse_new_segment (event, &is_update, &rate, &fmt, &start, &end, &base); GST_DEBUG_OBJECT (pad, "Got NEWSEGMENT event in %s format, passing on (%" GST_TIME_FORMAT " - %" GST_TIME_FORMAT ")", gst_format_get_name (fmt), GST_TIME_ARGS (start), GST_TIME_ARGS (end)); gst_segment_set_newsegment (&self->segment, is_update, rate, fmt, start, end, base); break; } case GST_EVENT_FLUSH_STOP: gst_segment_clip_reset (self); break; default: break; } } gst_object_unref (self); return ret; }
/* will be called in push mode */ static gboolean gst_app_src_do_seek (GstBaseSrc * src, GstSegment * segment) { GstAppSrc *appsrc = GST_APP_SRC_CAST (src); GstAppSrcPrivate *priv = appsrc->priv; gint64 desired_position; gboolean res = FALSE; desired_position = segment->position; GST_DEBUG_OBJECT (appsrc, "seeking to %" G_GINT64_FORMAT ", format %s", desired_position, gst_format_get_name (segment->format)); /* no need to try to seek in streaming mode */ if (priv->stream_type == GST_APP_STREAM_TYPE_STREAM) return TRUE; if (priv->callbacks.seek_data) res = priv->callbacks.seek_data (appsrc, desired_position, priv->user_data); else { gboolean emit; g_mutex_lock (&priv->mutex); emit = priv->emit_signals; g_mutex_unlock (&priv->mutex); if (emit) g_signal_emit (appsrc, gst_app_src_signals[SIGNAL_SEEK_DATA], 0, desired_position, &res); } if (res) { GST_DEBUG_OBJECT (appsrc, "flushing queue"); gst_app_src_flush_queued (appsrc); priv->is_eos = FALSE; } else { GST_WARNING_OBJECT (appsrc, "seek failed"); } return res; }
/** * gst_event_new_buffer_size: * @format: buffer format * @minsize: minimum buffer size * @maxsize: maximum buffer size * @async: thread behavior * * Create a new buffersize event. The event is sent downstream and notifies * elements that they should provide a buffer of the specified dimensions. * * When the @async flag is set, a thread boundary is preferred. * * Returns: (transfer full): a new #GstEvent */ GstEvent * gst_event_new_buffer_size (GstFormat format, gint64 minsize, gint64 maxsize, gboolean async) { GstEvent *event; GstStructure *structure; GST_CAT_INFO (GST_CAT_EVENT, "creating buffersize format %s, minsize %" G_GINT64_FORMAT ", maxsize %" G_GINT64_FORMAT ", async %d", gst_format_get_name (format), minsize, maxsize, async); structure = gst_structure_new_id (GST_QUARK (EVENT_BUFFER_SIZE), GST_QUARK (FORMAT), GST_TYPE_FORMAT, format, GST_QUARK (MINSIZE), G_TYPE_INT64, minsize, GST_QUARK (MAXSIZE), G_TYPE_INT64, maxsize, GST_QUARK (ASYNC), G_TYPE_BOOLEAN, async, NULL); event = gst_event_new_custom (GST_EVENT_BUFFERSIZE, structure); return event; }
/** * gst_event_new_seek: * @rate: The new playback rate * @format: The format of the seek values * @flags: The optional seek flags * @start_type: The type and flags for the new start position * @start: The value of the new start position * @stop_type: The type and flags for the new stop position * @stop: The value of the new stop position * * Allocate a new seek event with the given parameters. * * The seek event configures playback of the pipeline between @start to @stop * at the speed given in @rate, also called a playback segment. * The @start and @stop values are expressed in @format. * * A @rate of 1.0 means normal playback rate, 2.0 means double speed. * Negatives values means backwards playback. A value of 0.0 for the * rate is not allowed and should be accomplished instead by PAUSING the * pipeline. * * A pipeline has a default playback segment configured with a start * position of 0, a stop position of -1 and a rate of 1.0. The currently * configured playback segment can be queried with #GST_QUERY_SEGMENT. * * @start_type and @stop_type specify how to adjust the currently configured * start and stop fields in playback segment. Adjustments can be made relative * or absolute to the last configured values. A type of #GST_SEEK_TYPE_NONE * means that the position should not be updated. * * When the rate is positive and @start has been updated, playback will start * from the newly configured start position. * * For negative rates, playback will start from the newly configured stop * position (if any). If the stop position is updated, it must be different from * -1 (#GST_CLOCK_TIME_NONE) for negative rates. * * It is not possible to seek relative to the current playback position, to do * this, PAUSE the pipeline, query the current playback position with * #GST_QUERY_POSITION and update the playback segment current position with a * #GST_SEEK_TYPE_SET to the desired position. * * Returns: (transfer full): a new seek event. */ GstEvent * gst_event_new_seek (gdouble rate, GstFormat format, GstSeekFlags flags, GstSeekType start_type, gint64 start, GstSeekType stop_type, gint64 stop) { GstEvent *event; GstStructure *structure; g_return_val_if_fail (rate != 0.0, NULL); if (format == GST_FORMAT_TIME) { GST_CAT_INFO (GST_CAT_EVENT, "creating seek rate %lf, format TIME, flags %d, " "start_type %d, start %" GST_TIME_FORMAT ", " "stop_type %d, stop %" GST_TIME_FORMAT, rate, flags, start_type, GST_TIME_ARGS (start), stop_type, GST_TIME_ARGS (stop)); } else { GST_CAT_INFO (GST_CAT_EVENT, "creating seek rate %lf, format %s, flags %d, " "start_type %d, start %" G_GINT64_FORMAT ", " "stop_type %d, stop %" G_GINT64_FORMAT, rate, gst_format_get_name (format), flags, start_type, start, stop_type, stop); } structure = gst_structure_new_id (GST_QUARK (EVENT_SEEK), GST_QUARK (RATE), G_TYPE_DOUBLE, rate, GST_QUARK (FORMAT), GST_TYPE_FORMAT, format, GST_QUARK (FLAGS), GST_TYPE_SEEK_FLAGS, flags, GST_QUARK (CUR_TYPE), GST_TYPE_SEEK_TYPE, start_type, GST_QUARK (CUR), G_TYPE_INT64, start, GST_QUARK (STOP_TYPE), GST_TYPE_SEEK_TYPE, stop_type, GST_QUARK (STOP), G_TYPE_INT64, stop, NULL); event = gst_event_new_custom (GST_EVENT_SEEK, structure); return event; }
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; }
/* FIXME, the duration query should reflect how long you will produce * data, that is the amount of stream time until you will emit EOS. * * For synchronized mixing this is always the max of all the durations * of upstream since we emit EOS when all of them finished. * * We don't do synchronized mixing so this really depends on where the * streams where punched in and what their relative offsets are against * eachother which we can get from the first timestamps we see. * * When we add a new stream (or remove a stream) the duration might * also become invalid again and we need to post a new DURATION * message to notify this fact to the parent. * For now we take the max of all the upstream elements so the simple * cases work at least somewhat. */ static gboolean gst_adder_query_duration (GstAdder * adder, GstQuery * query) { gint64 max; gboolean res; GstFormat format; GstIterator *it; gboolean done; /* parse format */ gst_query_parse_duration (query, &format, NULL); max = -1; res = TRUE; done = FALSE; it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (adder)); while (!done) { GstIteratorResult ires; gpointer item; ires = gst_iterator_next (it, &item); switch (ires) { case GST_ITERATOR_DONE: done = TRUE; break; case GST_ITERATOR_OK: { GstPad *pad = GST_PAD_CAST (item); gint64 duration; /* ask sink peer for duration */ res &= gst_pad_query_peer_duration (pad, &format, &duration); /* take max from all valid return values */ if (res) { /* valid unknown length, stop searching */ if (duration == -1) { max = duration; done = TRUE; } /* else see if bigger than current max */ else if (duration > max) max = duration; } gst_object_unref (pad); break; } case GST_ITERATOR_RESYNC: max = -1; res = TRUE; gst_iterator_resync (it); break; default: res = FALSE; done = TRUE; break; } } gst_iterator_free (it); if (res) { /* and store the max */ GST_DEBUG_OBJECT (adder, "Total duration in format %s: %" GST_TIME_FORMAT, gst_format_get_name (format), GST_TIME_ARGS (max)); gst_query_set_duration (query, format, max); } return res; }
/** * gst_segment_set_newsegment_full: * @segment: a #GstSegment structure. * @update: flag indicating a new segment is started or updated * @rate: the rate of the segment. * @applied_rate: the applied rate of the segment. * @format: the format of the segment. * @start: the new start value * @stop: the new stop value * @time: the new stream time * * Update the segment structure with the field values of a new segment event. */ void gst_segment_set_newsegment_full (GstSegment * segment, gboolean update, gdouble rate, gdouble applied_rate, GstFormat format, gint64 start, gint64 stop, gint64 time) { gint64 duration, last_stop; g_return_if_fail (rate != 0.0); g_return_if_fail (applied_rate != 0.0); g_return_if_fail (segment != NULL); GST_DEBUG ("configuring segment update %d, rate %lf, format %s, " "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT ", position %" G_GINT64_FORMAT, update, rate, gst_format_get_name (format), start, stop, time); GST_DEBUG ("old segment was: %" GST_SEGMENT_FORMAT, segment); if (G_UNLIKELY (segment->format == GST_FORMAT_UNDEFINED)) segment->format = format; /* any other format with 0 also gives time 0, the other values are * invalid in the format though. */ if (format != segment->format && start == 0) { format = segment->format; if (stop != 0) stop = -1; if (time != 0) time = -1; } g_return_if_fail (segment->format == format); if (update) { if (G_LIKELY (segment->rate > 0.0)) { /* an update to the current segment is done, elapsed time is * difference between the old start and new start. */ if (start > segment->start) duration = start - segment->start; else duration = 0; } else { /* for negative rates, the elapsed duration is the diff between the stop * positions */ if (stop != -1 && stop < segment->stop) duration = segment->stop - stop; else duration = 0; } /* update last_stop to be a valid value in the updated segment */ if (start > segment->last_stop) last_stop = start; else if (stop != -1 && stop < segment->last_stop) last_stop = stop; else last_stop = segment->last_stop; } else { /* the new segment has to be aligned with the old segment. * We first update the accumulated time of the previous * segment. the accumulated time is used when syncing to the * clock. */ if (segment->stop != -1) { duration = segment->stop - segment->start; } else if (segment->last_stop != -1) { /* else use last seen timestamp as segment stop */ duration = segment->last_stop - segment->start; } else { /* else we don't know and throw a warning.. really, this should * be fixed in the element. */ g_warning ("closing segment of unknown duration, assuming duration of 0"); duration = 0; } /* position the last_stop to the next expected position in the new segment, * which is the start or the stop of the segment */ if (rate > 0.0) last_stop = start; else last_stop = stop; } /* use previous rate to calculate duration */ if (G_LIKELY (segment->abs_rate != 1.0)) duration /= segment->abs_rate; /* accumulate duration */ segment->accum += duration; /* then update the current segment */ segment->rate = rate; segment->abs_rate = ABS (rate); segment->applied_rate = applied_rate; segment->start = start; segment->last_stop = last_stop; segment->stop = stop; segment->time = time; }
/** * gst_video_info_convert: * @info: a #GstVideoInfo * @src_format: #GstFormat of the @src_value * @src_value: value to convert * @dest_format: #GstFormat of the @dest_value * @dest_value: pointer to destination value * * Converts among various #GstFormat types. This function handles * GST_FORMAT_BYTES, GST_FORMAT_TIME, and GST_FORMAT_DEFAULT. For * raw video, GST_FORMAT_DEFAULT corresponds to video frames. This * function can be used to handle pad queries of the type GST_QUERY_CONVERT. * * Returns: TRUE if the conversion was successful. */ gboolean gst_video_info_convert (GstVideoInfo * info, GstFormat src_format, gint64 src_value, GstFormat dest_format, gint64 * dest_value) { gboolean ret = FALSE; int fps_n, fps_d; gsize size; g_return_val_if_fail (info != NULL, 0); g_return_val_if_fail (info->finfo != NULL, 0); g_return_val_if_fail (info->finfo->format != GST_VIDEO_FORMAT_UNKNOWN, 0); g_return_val_if_fail (info->size > 0, 0); size = info->size; fps_n = info->fps_n; fps_d = info->fps_d; GST_DEBUG ("converting value %" G_GINT64_FORMAT " from %s to %s", src_value, gst_format_get_name (src_format), gst_format_get_name (dest_format)); if (src_format == dest_format) { *dest_value = src_value; ret = TRUE; goto done; } if (src_value == -1) { *dest_value = -1; ret = TRUE; goto done; } /* bytes to frames */ if (src_format == GST_FORMAT_BYTES && dest_format == GST_FORMAT_DEFAULT) { if (size != 0) { *dest_value = gst_util_uint64_scale (src_value, 1, size); } else { GST_ERROR ("blocksize is 0"); *dest_value = 0; } ret = TRUE; goto done; } /* frames to bytes */ if (src_format == GST_FORMAT_DEFAULT && dest_format == GST_FORMAT_BYTES) { *dest_value = gst_util_uint64_scale (src_value, size, 1); ret = TRUE; goto done; } /* time to frames */ if (src_format == GST_FORMAT_TIME && dest_format == GST_FORMAT_DEFAULT) { if (fps_d != 0) { *dest_value = gst_util_uint64_scale (src_value, fps_n, GST_SECOND * fps_d); } else { GST_ERROR ("framerate denominator is 0"); *dest_value = 0; } ret = TRUE; goto done; } /* frames to time */ if (src_format == GST_FORMAT_DEFAULT && dest_format == GST_FORMAT_TIME) { if (fps_n != 0) { *dest_value = gst_util_uint64_scale (src_value, GST_SECOND * fps_d, fps_n); } else { GST_ERROR ("framerate numerator is 0"); *dest_value = 0; } ret = TRUE; goto done; } /* time to bytes */ if (src_format == GST_FORMAT_TIME && dest_format == GST_FORMAT_BYTES) { if (fps_d != 0) { *dest_value = gst_util_uint64_scale (src_value, fps_n * size, GST_SECOND * fps_d); } else { GST_ERROR ("framerate denominator is 0"); *dest_value = 0; } ret = TRUE; goto done; } /* bytes to time */ if (src_format == GST_FORMAT_BYTES && dest_format == GST_FORMAT_TIME) { if (fps_n != 0 && size != 0) { *dest_value = gst_util_uint64_scale (src_value, GST_SECOND * fps_d, fps_n * size); } else { GST_ERROR ("framerate denominator and/or blocksize is 0"); *dest_value = 0; } ret = TRUE; } done: GST_DEBUG ("ret=%d result %" G_GINT64_FORMAT, ret, *dest_value); return ret; }
/* sinkpad functions */ static gboolean gst_stream_synchronizer_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) { GstStreamSynchronizer *self = GST_STREAM_SYNCHRONIZER (parent); GstPad *opad; gboolean ret = FALSE; GST_LOG_OBJECT (pad, "Handling event %s: %" GST_PTR_FORMAT, GST_EVENT_TYPE_NAME (event), event); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_STREAM_START: { GstStream *stream, *ostream; guint32 seqnum = gst_event_get_seqnum (event); GList *l; gboolean all_wait = TRUE; gboolean new_stream = TRUE; GST_STREAM_SYNCHRONIZER_LOCK (self); stream = gst_pad_get_element_private (pad); if (stream && stream->stream_start_seqnum != seqnum) { stream->is_eos = FALSE; stream->stream_start_seqnum = seqnum; stream->drop_discont = TRUE; /* Check if this belongs to a stream that is already there, * e.g. we got the visualizations for an audio stream */ for (l = self->streams; l; l = l->next) { ostream = l->data; if (ostream != stream && ostream->stream_start_seqnum == seqnum && !ostream->wait) { new_stream = FALSE; break; } } if (!new_stream) { GST_DEBUG_OBJECT (pad, "Stream %d belongs to running stream %d, no waiting", stream->stream_number, ostream->stream_number); stream->wait = FALSE; stream->new_stream = FALSE; } else { GST_DEBUG_OBJECT (pad, "Stream %d changed", stream->stream_number); stream->wait = TRUE; stream->new_stream = TRUE; for (l = self->streams; l; l = l->next) { GstStream *ostream = l->data; all_wait = all_wait && ostream->wait; if (!all_wait) break; } if (all_wait) { gint64 position = 0; GST_DEBUG_OBJECT (self, "All streams have changed -- unblocking"); for (l = self->streams; l; l = l->next) { GstStream *ostream = l->data; gint64 stop_running_time; gint64 position_running_time; ostream->wait = FALSE; if (ostream->segment.format == GST_FORMAT_TIME) { stop_running_time = gst_segment_to_running_time (&ostream->segment, GST_FORMAT_TIME, ostream->segment.stop); position_running_time = gst_segment_to_running_time (&ostream->segment, GST_FORMAT_TIME, ostream->segment.position); position = MAX (position, MAX (stop_running_time, position_running_time)); } } position = MAX (0, position); self->group_start_time = MAX (self->group_start_time, position); GST_DEBUG_OBJECT (self, "New group start time: %" GST_TIME_FORMAT, GST_TIME_ARGS (self->group_start_time)); for (l = self->streams; l; l = l->next) { GstStream *ostream = l->data; g_cond_broadcast (&ostream->stream_finish_cond); } } } } else { GST_DEBUG_OBJECT (self, "No stream or STREAM_START from same source"); } GST_STREAM_SYNCHRONIZER_UNLOCK (self); break; } case GST_EVENT_SEGMENT:{ GstStream *stream; GstSegment segment; gst_event_copy_segment (event, &segment); GST_STREAM_SYNCHRONIZER_LOCK (self); stream = gst_pad_get_element_private (pad); if (stream) { if (stream->wait) { GST_DEBUG_OBJECT (pad, "Stream %d is waiting", stream->stream_number); g_cond_wait (&stream->stream_finish_cond, &self->lock); stream = gst_pad_get_element_private (pad); if (stream) stream->wait = FALSE; } } if (self->shutdown) { GST_STREAM_SYNCHRONIZER_UNLOCK (self); gst_event_unref (event); goto done; } if (stream && segment.format == GST_FORMAT_TIME) { if (stream->new_stream) { stream->new_stream = FALSE; segment.base = self->group_start_time; } GST_DEBUG_OBJECT (pad, "Segment was: %" GST_SEGMENT_FORMAT, &stream->segment); gst_segment_copy_into (&segment, &stream->segment); GST_DEBUG_OBJECT (pad, "Segment now is: %" GST_SEGMENT_FORMAT, &stream->segment); stream->segment_seqnum = gst_event_get_seqnum (event); GST_DEBUG_OBJECT (pad, "Stream start running time: %" GST_TIME_FORMAT, GST_TIME_ARGS (stream->segment.base)); { GstEvent *tmpev; tmpev = gst_event_new_segment (&stream->segment); gst_event_set_seqnum (tmpev, stream->segment_seqnum); gst_event_unref (event); event = tmpev; } } else if (stream) { GST_WARNING_OBJECT (pad, "Non-TIME segment: %s", gst_format_get_name (segment.format)); gst_segment_init (&stream->segment, GST_FORMAT_UNDEFINED); /* Since this stream is not time-based, we mark it so that * other streams don't wait forever on it */ stream->wait = TRUE; } GST_STREAM_SYNCHRONIZER_UNLOCK (self); break; } case GST_EVENT_FLUSH_START:{ GstStream *stream; GST_STREAM_SYNCHRONIZER_LOCK (self); stream = gst_pad_get_element_private (pad); if (stream) { GST_DEBUG_OBJECT (pad, "Flushing streams"); g_cond_broadcast (&stream->stream_finish_cond); } GST_STREAM_SYNCHRONIZER_UNLOCK (self); break; } case GST_EVENT_FLUSH_STOP:{ GstStream *stream; GST_STREAM_SYNCHRONIZER_LOCK (self); stream = gst_pad_get_element_private (pad); if (stream) { GST_DEBUG_OBJECT (pad, "Resetting segment for stream %d", stream->stream_number); gst_segment_init (&stream->segment, GST_FORMAT_UNDEFINED); stream->is_eos = FALSE; stream->wait = FALSE; stream->new_stream = FALSE; stream->drop_discont = FALSE; stream->seen_data = FALSE; g_cond_broadcast (&stream->stream_finish_cond); } GST_STREAM_SYNCHRONIZER_UNLOCK (self); break; } case GST_EVENT_EOS:{ GstStream *stream; GList *l; gboolean all_eos = TRUE; gboolean seen_data; GSList *pads = NULL; GstPad *srcpad; GstClockTime timestamp; GST_STREAM_SYNCHRONIZER_LOCK (self); stream = gst_pad_get_element_private (pad); if (!stream) { GST_STREAM_SYNCHRONIZER_UNLOCK (self); GST_WARNING_OBJECT (pad, "EOS for unknown stream"); break; } GST_DEBUG_OBJECT (pad, "Have EOS for stream %d", stream->stream_number); stream->is_eos = TRUE; seen_data = stream->seen_data; srcpad = gst_object_ref (stream->srcpad); if (seen_data && stream->segment.position != -1) timestamp = stream->segment.position; else if (stream->segment.rate < 0.0 || stream->segment.stop == -1) timestamp = stream->segment.start; else timestamp = stream->segment.stop; for (l = self->streams; l; l = l->next) { GstStream *ostream = l->data; all_eos = all_eos && ostream->is_eos; if (!all_eos) break; } if (all_eos) { GST_DEBUG_OBJECT (self, "All streams are EOS -- forwarding"); for (l = self->streams; l; l = l->next) { GstStream *ostream = l->data; /* local snapshot of current pads */ gst_object_ref (ostream->srcpad); pads = g_slist_prepend (pads, ostream->srcpad); } } GST_STREAM_SYNCHRONIZER_UNLOCK (self); /* drop lock when sending eos, which may block in e.g. preroll */ if (pads) { GstPad *pad; GSList *epad; ret = TRUE; epad = pads; while (epad) { pad = epad->data; GST_DEBUG_OBJECT (pad, "Pushing EOS"); ret = ret && gst_pad_push_event (pad, gst_event_new_eos ()); gst_object_unref (pad); epad = g_slist_next (epad); } g_slist_free (pads); } else { /* if EOS, but no data has passed, then send something to replace EOS * for preroll purposes */ if (!seen_data) { GstEvent *gap_event; gap_event = gst_event_new_gap (timestamp, GST_CLOCK_TIME_NONE); ret = gst_pad_push_event (srcpad, gap_event); } else { GstEvent *gap_event; /* FIXME: Also send a GAP event to let audio sinks start their * clock in case they did not have enough data yet */ gap_event = gst_event_new_gap (timestamp, GST_CLOCK_TIME_NONE); ret = gst_pad_push_event (srcpad, gap_event); } } gst_object_unref (srcpad); gst_event_unref (event); goto done; } default: break; } opad = gst_stream_get_other_pad_from_pad (self, pad); if (opad) { ret = gst_pad_push_event (opad, event); gst_object_unref (opad); } done: return ret; }
static gboolean gst_interleave_src_query_duration (GstInterleave * self, GstQuery * query) { gint64 max; gboolean res; GstFormat format; GstIterator *it; gboolean done; /* parse format */ gst_query_parse_duration (query, &format, NULL); max = -1; res = TRUE; done = FALSE; /* Take maximum of all durations */ it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (self)); while (!done) { GstIteratorResult ires; GValue item = { 0, }; ires = gst_iterator_next (it, &item); switch (ires) { case GST_ITERATOR_DONE: done = TRUE; break; case GST_ITERATOR_OK: { GstPad *pad = GST_PAD_CAST (g_value_dup_object (&item)); gint64 duration; /* ask sink peer for duration */ res &= gst_pad_peer_query_duration (pad, format, &duration); /* take max from all valid return values */ if (res) { /* valid unknown length, stop searching */ if (duration == -1) { max = duration; done = TRUE; } /* else see if bigger than current max */ else if (duration > max) max = duration; } gst_object_unref (pad); g_value_unset (&item); break; } case GST_ITERATOR_RESYNC: max = -1; res = TRUE; gst_iterator_resync (it); break; default: res = FALSE; done = TRUE; break; } } gst_iterator_free (it); if (res) { /* If in bytes format we have to multiply with the number of channels * to get the correct results. All other formats should be fine */ if (format == GST_FORMAT_BYTES && max != -1) max *= self->channels; /* and store the max */ GST_DEBUG_OBJECT (self, "Total duration in format %s: %" GST_TIME_FORMAT, gst_format_get_name (format), GST_TIME_ARGS (max)); gst_query_set_duration (query, format, max); } return res; }
static gboolean gst_aiff_parse_pad_convert (GstPad * pad, GstFormat src_format, gint64 src_value, GstFormat * dest_format, gint64 * dest_value) { GstAiffParse *aiffparse; gboolean res = TRUE; aiffparse = GST_AIFF_PARSE (GST_PAD_PARENT (pad)); if (*dest_format == src_format) { *dest_value = src_value; return TRUE; } if (aiffparse->bytes_per_sample <= 0) return FALSE; GST_INFO_OBJECT (aiffparse, "converting value from %s to %s", gst_format_get_name (src_format), gst_format_get_name (*dest_format)); switch (src_format) { case GST_FORMAT_BYTES: switch (*dest_format) { case GST_FORMAT_DEFAULT: *dest_value = src_value / aiffparse->bytes_per_sample; break; case GST_FORMAT_TIME: if (aiffparse->bps > 0) { *dest_value = uint64_ceiling_scale (src_value, GST_SECOND, (guint64) aiffparse->bps); break; } /* Else fallthrough */ default: res = FALSE; goto done; } break; case GST_FORMAT_DEFAULT: switch (*dest_format) { case GST_FORMAT_BYTES: *dest_value = src_value * aiffparse->bytes_per_sample; break; case GST_FORMAT_TIME: *dest_value = gst_util_uint64_scale (src_value, GST_SECOND, (guint64) aiffparse->rate); break; default: res = FALSE; goto done; } break; case GST_FORMAT_TIME: switch (*dest_format) { case GST_FORMAT_BYTES: if (aiffparse->bps > 0) { *dest_value = gst_util_uint64_scale (src_value, (guint64) aiffparse->bps, GST_SECOND); break; } /* Else fallthrough */ break; case GST_FORMAT_DEFAULT: *dest_value = gst_util_uint64_scale (src_value, (guint64) aiffparse->rate, GST_SECOND); break; default: res = FALSE; goto done; } break; default: res = FALSE; goto done; } done: return res; }
static gboolean gst_tag_lib_mux_sink_event (GstPad * pad, GstEvent * event) { GstTagLibMux *mux; gboolean result; mux = GST_TAG_LIB_MUX (gst_pad_get_parent (pad)); result = FALSE; switch (GST_EVENT_TYPE (event)) { case GST_EVENT_TAG:{ GstTagList *tags; gst_event_parse_tag (event, &tags); GST_INFO_OBJECT (mux, "Got tag event: %" GST_PTR_FORMAT, tags); if (mux->event_tags != NULL) { gst_tag_list_insert (mux->event_tags, tags, GST_TAG_MERGE_REPLACE); } else { mux->event_tags = gst_tag_list_copy (tags); } GST_INFO_OBJECT (mux, "Event tags are now: %" GST_PTR_FORMAT, mux->event_tags); /* just drop the event, we'll push a new tag event in render_tag */ gst_event_unref (event); result = TRUE; break; } case GST_EVENT_NEWSEGMENT:{ GstFormat fmt; gst_event_parse_new_segment (event, NULL, NULL, &fmt, NULL, NULL, NULL); if (fmt != GST_FORMAT_BYTES) { GST_WARNING_OBJECT (mux, "dropping newsegment event in %s format", gst_format_get_name (fmt)); gst_event_unref (event); break; } if (mux->render_tag) { /* we have not rendered the tag yet, which means that we don't know * how large it is going to be yet, so we can't adjust the offsets * here at this point and need to cache the newsegment event for now * (also, there could be tag events coming after this newsegment event * and before the first buffer). */ if (mux->newsegment_ev) { GST_WARNING_OBJECT (mux, "discarding old cached newsegment event"); gst_event_unref (mux->newsegment_ev); } GST_LOG_OBJECT (mux, "caching newsegment event for later"); mux->newsegment_ev = event; } else { GST_DEBUG_OBJECT (mux, "got newsegment event, adjusting offsets"); gst_pad_push_event (mux->srcpad, gst_tag_lib_mux_adjust_event_offsets (mux, event)); gst_event_unref (event); } event = NULL; result = TRUE; break; } default: result = gst_pad_event_default (pad, event); break; } gst_object_unref (mux); return result; }
static gboolean gst_raw_base_parse_convert (GstBaseParse * parse, GstFormat src_format, gint64 src_value, GstFormat dest_format, gint64 * dest_value) { GstRawBaseParse *raw_base_parse = GST_RAW_BASE_PARSE (parse); GstRawBaseParseClass *klass = GST_RAW_BASE_PARSE_GET_CLASS (parse); gboolean ret = TRUE; gsize units_n, units_d; g_assert (klass->is_config_ready); g_assert (klass->get_units_per_second); /* The operations below access the current config. Protect * against race conditions by using the object lock. */ GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (raw_base_parse); if (!klass->is_config_ready (raw_base_parse, GST_RAW_BASE_PARSE_CONFIG_CURRENT)) { if (gst_raw_base_parse_is_using_sink_caps (raw_base_parse)) { goto config_not_ready; } else { /* This should not be reached if the property config is active */ g_assert_not_reached (); } } if (G_UNLIKELY (src_format == dest_format)) { *dest_value = src_value; } else if ((src_format == GST_FORMAT_TIME || dest_format == GST_FORMAT_TIME) && gst_raw_base_parse_is_gstformat_supported (raw_base_parse, src_format) && gst_raw_base_parse_is_gstformat_supported (raw_base_parse, src_format)) { /* Perform conversions here if either the src or dest format * are GST_FORMAT_TIME and the other format is supported by * the subclass. This is because we perform TIME<->non-TIME * conversions here. Typically, subclasses only support * BYTES and DEFAULT formats. */ if (src_format == GST_FORMAT_TIME) { /* The source format is time, so perform a TIME -> non-TIME conversion */ klass->get_units_per_second (raw_base_parse, dest_format, GST_RAW_BASE_PARSE_CONFIG_CURRENT, &units_n, &units_d); *dest_value = (units_n == 0 || units_d == 0) ? src_value : gst_util_uint64_scale (src_value, units_n, GST_SECOND * units_d); } else { /* The dest format is time, so perform a non-TIME -> TIME conversion */ klass->get_units_per_second (raw_base_parse, src_format, GST_RAW_BASE_PARSE_CONFIG_CURRENT, &units_n, &units_d); *dest_value = (units_n == 0 || units_d == 0) ? src_value : gst_util_uint64_scale (src_value, GST_SECOND * units_d, units_n); } } else { /* Fallback for other conversions */ ret = gst_base_parse_convert_default (parse, src_format, src_value, dest_format, dest_value); } GST_DEBUG_OBJECT (parse, "converted %s -> %s %" G_GINT64_FORMAT " -> %" GST_TIME_FORMAT, gst_format_get_name (src_format), gst_format_get_name (dest_format), src_value, GST_TIME_ARGS (*dest_value)); GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (raw_base_parse); return ret; config_not_ready: GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (raw_base_parse); GST_ELEMENT_ERROR (parse, STREAM, FORMAT, ("sink caps config is the current config, and it is not ready - " "upstream may not have pushed a caps event yet"), (NULL)); return FALSE; }
/* sinkpad functions */ static gboolean gst_stream_synchronizer_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) { GstStreamSynchronizer *self = GST_STREAM_SYNCHRONIZER (parent); gboolean ret = FALSE; GST_LOG_OBJECT (pad, "Handling event %s: %" GST_PTR_FORMAT, GST_EVENT_TYPE_NAME (event), event); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_STREAM_START: { GstSyncStream *stream, *ostream; guint32 seqnum = gst_event_get_seqnum (event); guint group_id; gboolean have_group_id; GList *l; gboolean all_wait = TRUE; gboolean new_stream = TRUE; have_group_id = gst_event_parse_group_id (event, &group_id); GST_STREAM_SYNCHRONIZER_LOCK (self); self->have_group_id &= have_group_id; have_group_id = self->have_group_id; stream = gst_pad_get_element_private (pad); if (!stream) { GST_DEBUG_OBJECT (self, "No stream or STREAM_START from same source"); GST_STREAM_SYNCHRONIZER_UNLOCK (self); break; } gst_event_parse_stream_flags (event, &stream->flags); if ((have_group_id && stream->group_id != group_id) || (!have_group_id && stream->stream_start_seqnum != seqnum)) { stream->is_eos = FALSE; stream->eos_sent = FALSE; stream->flushing = FALSE; stream->stream_start_seqnum = seqnum; stream->group_id = group_id; if (!have_group_id) { /* Check if this belongs to a stream that is already there, * e.g. we got the visualizations for an audio stream */ for (l = self->streams; l; l = l->next) { ostream = l->data; if (ostream != stream && ostream->stream_start_seqnum == seqnum && !ostream->wait) { new_stream = FALSE; break; } } if (!new_stream) { GST_DEBUG_OBJECT (pad, "Stream %d belongs to running stream %d, no waiting", stream->stream_number, ostream->stream_number); stream->wait = FALSE; GST_STREAM_SYNCHRONIZER_UNLOCK (self); break; } } else if (group_id == self->group_id) { GST_DEBUG_OBJECT (pad, "Stream %d belongs to running group %d, " "no waiting", stream->stream_number, group_id); GST_STREAM_SYNCHRONIZER_UNLOCK (self); break; } GST_DEBUG_OBJECT (pad, "Stream %d changed", stream->stream_number); stream->wait = TRUE; for (l = self->streams; l; l = l->next) { GstSyncStream *ostream = l->data; all_wait = all_wait && ((ostream->flags & GST_STREAM_FLAG_SPARSE) || (ostream->wait && (!have_group_id || ostream->group_id == group_id))); if (!all_wait) break; } if (all_wait) { gint64 position = 0; if (have_group_id) GST_DEBUG_OBJECT (self, "All streams have changed to group id %u -- unblocking", group_id); else GST_DEBUG_OBJECT (self, "All streams have changed -- unblocking"); self->group_id = group_id; for (l = self->streams; l; l = l->next) { GstSyncStream *ostream = l->data; gint64 stop_running_time; gint64 position_running_time; ostream->wait = FALSE; if (ostream->segment.format == GST_FORMAT_TIME) { stop_running_time = gst_segment_to_running_time (&ostream->segment, GST_FORMAT_TIME, ostream->segment.stop); position_running_time = gst_segment_to_running_time (&ostream->segment, GST_FORMAT_TIME, ostream->segment.position); position_running_time = MAX (position_running_time, stop_running_time); position_running_time -= gst_segment_to_running_time (&ostream->segment, GST_FORMAT_TIME, ostream->segment.start); position_running_time = MAX (0, position_running_time); position = MAX (position, position_running_time); } } self->group_start_time += position; GST_DEBUG_OBJECT (self, "New group start time: %" GST_TIME_FORMAT, GST_TIME_ARGS (self->group_start_time)); for (l = self->streams; l; l = l->next) { GstSyncStream *ostream = l->data; ostream->wait = FALSE; g_cond_broadcast (&ostream->stream_finish_cond); } } } GST_STREAM_SYNCHRONIZER_UNLOCK (self); break; } case GST_EVENT_SEGMENT:{ GstSyncStream *stream; GstSegment segment; gst_event_copy_segment (event, &segment); GST_STREAM_SYNCHRONIZER_LOCK (self); gst_stream_synchronizer_wait (self, pad); if (self->shutdown) { GST_STREAM_SYNCHRONIZER_UNLOCK (self); gst_event_unref (event); goto done; } stream = gst_pad_get_element_private (pad); if (stream && segment.format == GST_FORMAT_TIME) { GST_DEBUG_OBJECT (pad, "New stream, updating base from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT, GST_TIME_ARGS (segment.base), GST_TIME_ARGS (segment.base + self->group_start_time)); segment.base += self->group_start_time; GST_DEBUG_OBJECT (pad, "Segment was: %" GST_SEGMENT_FORMAT, &stream->segment); gst_segment_copy_into (&segment, &stream->segment); GST_DEBUG_OBJECT (pad, "Segment now is: %" GST_SEGMENT_FORMAT, &stream->segment); stream->segment_seqnum = gst_event_get_seqnum (event); GST_DEBUG_OBJECT (pad, "Stream start running time: %" GST_TIME_FORMAT, GST_TIME_ARGS (stream->segment.base)); { GstEvent *tmpev; tmpev = gst_event_new_segment (&stream->segment); gst_event_set_seqnum (tmpev, stream->segment_seqnum); gst_event_unref (event); event = tmpev; } } else if (stream) { GST_WARNING_OBJECT (pad, "Non-TIME segment: %s", gst_format_get_name (segment.format)); gst_segment_init (&stream->segment, GST_FORMAT_UNDEFINED); } GST_STREAM_SYNCHRONIZER_UNLOCK (self); break; } case GST_EVENT_FLUSH_START:{ GstSyncStream *stream; GST_STREAM_SYNCHRONIZER_LOCK (self); stream = gst_pad_get_element_private (pad); self->eos = FALSE; if (stream) { GST_DEBUG_OBJECT (pad, "Flushing streams"); stream->flushing = TRUE; g_cond_broadcast (&stream->stream_finish_cond); } GST_STREAM_SYNCHRONIZER_UNLOCK (self); break; } case GST_EVENT_FLUSH_STOP:{ GstSyncStream *stream; GList *l; GstClockTime new_group_start_time = 0; GST_STREAM_SYNCHRONIZER_LOCK (self); stream = gst_pad_get_element_private (pad); if (stream) { GST_DEBUG_OBJECT (pad, "Resetting segment for stream %d", stream->stream_number); gst_segment_init (&stream->segment, GST_FORMAT_UNDEFINED); stream->is_eos = FALSE; stream->eos_sent = FALSE; stream->flushing = FALSE; stream->wait = FALSE; g_cond_broadcast (&stream->stream_finish_cond); } for (l = self->streams; l; l = l->next) { GstSyncStream *ostream = l->data; GstClockTime start_running_time; if (ostream == stream || ostream->flushing) continue; if (ostream->segment.format == GST_FORMAT_TIME) { start_running_time = gst_segment_to_running_time (&ostream->segment, GST_FORMAT_TIME, ostream->segment.start); new_group_start_time = MAX (new_group_start_time, start_running_time); } } GST_DEBUG_OBJECT (pad, "Updating group start time from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT, GST_TIME_ARGS (self->group_start_time), GST_TIME_ARGS (new_group_start_time)); self->group_start_time = new_group_start_time; GST_STREAM_SYNCHRONIZER_UNLOCK (self); break; } /* unblocking EOS wait when track switch. */ case GST_EVENT_CUSTOM_DOWNSTREAM_OOB:{ if (gst_event_has_name (event, "playsink-custom-video-flush") || gst_event_has_name (event, "playsink-custom-audio-flush") || gst_event_has_name (event, "playsink-custom-subtitle-flush")) { GstSyncStream *stream; GST_STREAM_SYNCHRONIZER_LOCK (self); stream = gst_pad_get_element_private (pad); if (stream) { stream->is_eos = FALSE; stream->eos_sent = FALSE; stream->wait = FALSE; g_cond_broadcast (&stream->stream_finish_cond); } GST_STREAM_SYNCHRONIZER_UNLOCK (self); } break; } case GST_EVENT_EOS:{ GstSyncStream *stream; GList *l; gboolean all_eos = TRUE; gboolean seen_data; GSList *pads = NULL; GstPad *srcpad; GstClockTime timestamp; GST_STREAM_SYNCHRONIZER_LOCK (self); stream = gst_pad_get_element_private (pad); if (!stream) { GST_STREAM_SYNCHRONIZER_UNLOCK (self); GST_WARNING_OBJECT (pad, "EOS for unknown stream"); break; } GST_DEBUG_OBJECT (pad, "Have EOS for stream %d", stream->stream_number); stream->is_eos = TRUE; seen_data = stream->seen_data; srcpad = gst_object_ref (stream->srcpad); if (seen_data && stream->segment.position != -1) timestamp = stream->segment.position; else if (stream->segment.rate < 0.0 || stream->segment.stop == -1) timestamp = stream->segment.start; else timestamp = stream->segment.stop; stream->segment.position = timestamp; for (l = self->streams; l; l = l->next) { GstSyncStream *ostream = l->data; all_eos = all_eos && ostream->is_eos; if (!all_eos) break; } if (all_eos) { GST_DEBUG_OBJECT (self, "All streams are EOS -- forwarding"); self->eos = TRUE; for (l = self->streams; l; l = l->next) { GstSyncStream *ostream = l->data; /* local snapshot of current pads */ gst_object_ref (ostream->srcpad); pads = g_slist_prepend (pads, ostream->srcpad); } } if (pads) { GstPad *pad; GSList *epad; GstSyncStream *ostream; ret = TRUE; epad = pads; while (epad) { pad = epad->data; ostream = gst_pad_get_element_private (pad); if (ostream) { g_cond_broadcast (&ostream->stream_finish_cond); } gst_object_unref (pad); epad = g_slist_next (epad); } g_slist_free (pads); } else { if (seen_data) { self->send_gap_event = TRUE; stream->gap_duration = GST_CLOCK_TIME_NONE; stream->wait = TRUE; ret = gst_stream_synchronizer_wait (self, srcpad); } } /* send eos if haven't seen data. seen_data will be true if data buffer * of the track have received in anytime. sink is ready if seen_data is * true, so can send GAP event. Will send EOS if sink isn't ready. The * scenario for the case is one track haven't any media data and then * send EOS. Or no any valid media data in one track, so decoder can't * get valid CAPS for the track. sink can't ready without received CAPS.*/ if (!seen_data || self->eos) { GST_DEBUG_OBJECT (pad, "send EOS event"); /* drop lock when sending eos, which may block in e.g. preroll */ GST_STREAM_SYNCHRONIZER_UNLOCK (self); ret = gst_pad_push_event (srcpad, gst_event_new_eos ()); GST_STREAM_SYNCHRONIZER_LOCK (self); stream = gst_pad_get_element_private (pad); if (stream) { stream->eos_sent = TRUE; } } gst_object_unref (srcpad); gst_event_unref (event); GST_STREAM_SYNCHRONIZER_UNLOCK (self); goto done; } default: break; } ret = gst_pad_event_default (pad, parent, event); done: return ret; }
static gboolean theora_parse_src_convert (GstPad * pad, GstFormat src_format, gint64 src_value, GstFormat * dest_format, gint64 * dest_value) { gboolean res = TRUE; GstTheoraParse *parse; guint64 scale = 1; if (src_format == *dest_format) { *dest_value = src_value; return TRUE; } parse = GST_THEORA_PARSE (gst_pad_get_parent (pad)); /* we need the info part before we can done something */ if (!parse->streamheader_received) goto no_header; switch (src_format) { case GST_FORMAT_BYTES: switch (*dest_format) { case GST_FORMAT_DEFAULT: *dest_value = gst_util_uint64_scale_int (src_value, 2, parse->info.pic_height * parse->info.pic_width * 3); break; case GST_FORMAT_TIME: /* seems like a rather silly conversion, implement me if you like */ default: res = FALSE; } break; case GST_FORMAT_TIME: switch (*dest_format) { case GST_FORMAT_BYTES: scale = 3 * (parse->info.pic_width * parse->info.pic_height) / 2; case GST_FORMAT_DEFAULT: *dest_value = scale * gst_util_uint64_scale (src_value, parse->info.fps_numerator, parse->info.fps_denominator * GST_SECOND); break; default: GST_DEBUG_OBJECT (parse, "cannot convert to format %s", gst_format_get_name (*dest_format)); res = FALSE; } break; case GST_FORMAT_DEFAULT: switch (*dest_format) { case GST_FORMAT_TIME: *dest_value = gst_util_uint64_scale (src_value, GST_SECOND * parse->info.fps_denominator, parse->info.fps_numerator); break; case GST_FORMAT_BYTES: *dest_value = gst_util_uint64_scale_int (src_value, 3 * parse->info.pic_width * parse->info.pic_height, 2); break; default: res = FALSE; } break; default: res = FALSE; } done: gst_object_unref (parse); return res; /* ERRORS */ no_header: { GST_DEBUG_OBJECT (parse, "no header yet, cannot convert"); res = FALSE; goto done; } }
static gboolean theora_parse_src_query (GstPad * pad, GstObject * parent, GstQuery * query) { GstTheoraParse *parse; gboolean res = FALSE; parse = GST_THEORA_PARSE (parent); switch (GST_QUERY_TYPE (query)) { case GST_QUERY_POSITION: { gint64 frame, value; GstFormat my_format, format; gint64 time; frame = parse->prev_frame; GST_LOG_OBJECT (parse, "query %p: we have current frame: %" G_GINT64_FORMAT, query, frame); /* parse format */ gst_query_parse_position (query, &format, NULL); /* and convert to the final format in two steps with time as the * intermediate step */ my_format = GST_FORMAT_TIME; if (!(res = theora_parse_src_convert (parse->sinkpad, GST_FORMAT_DEFAULT, frame, &my_format, &time))) goto error; /* fixme: handle segments time = (time - parse->segment.start) + parse->segment.time; */ GST_LOG_OBJECT (parse, "query %p: our time: %" GST_TIME_FORMAT " (conv to %s)", query, GST_TIME_ARGS (time), gst_format_get_name (format)); if (!(res = theora_parse_src_convert (pad, my_format, time, &format, &value))) goto error; gst_query_set_position (query, format, value); GST_LOG_OBJECT (parse, "query %p: we return %" G_GINT64_FORMAT " (format %u)", query, value, format); break; } case GST_QUERY_DURATION: /* forward to peer for total */ if (!(res = gst_pad_query (GST_PAD_PEER (parse->sinkpad), query))) goto error; break; case GST_QUERY_CONVERT: { GstFormat src_fmt, dest_fmt; gint64 src_val, dest_val; gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val); if (!(res = theora_parse_src_convert (pad, src_fmt, src_val, &dest_fmt, &dest_val))) goto error; gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); break; } default: res = gst_pad_query_default (pad, parent, query); break; } done: return res; /* ERRORS */ error: { GST_DEBUG_OBJECT (parse, "query failed"); goto done; } }
/* 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; } }
static gboolean gst_frei0r_mixer_src_query_duration (GstFrei0rMixer * self, GstQuery * query) { gint64 min; gboolean res; GstFormat format; GstIterator *it; gboolean done; /* parse format */ gst_query_parse_duration (query, &format, NULL); min = -1; res = TRUE; done = FALSE; /* Take minimum of all durations */ it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (self)); while (!done) { GstIteratorResult ires; GValue item = { 0 }; ires = gst_iterator_next (it, &item); switch (ires) { case GST_ITERATOR_DONE: done = TRUE; break; case GST_ITERATOR_OK: { GstPad *pad = g_value_get_object (&item); gint64 duration; /* ask sink peer for duration */ res &= gst_pad_peer_query_duration (pad, format, &duration); /* take min from all valid return values */ if (res) { /* valid unknown length, stop searching */ if (duration == -1) { min = duration; done = TRUE; } /* else see if smaller than current min */ else if (duration < min) min = duration; } g_value_reset (&item); break; } case GST_ITERATOR_RESYNC: min = -1; res = TRUE; gst_iterator_resync (it); break; default: res = FALSE; done = TRUE; break; } g_value_unset (&item); } gst_iterator_free (it); if (res) { /* and store the min */ GST_DEBUG_OBJECT (self, "Total duration in format %s: %" GST_TIME_FORMAT, gst_format_get_name (format), GST_TIME_ARGS (min)); gst_query_set_duration (query, format, min); } return res; }