/** * gst_event_new_custom: * @type: The type of the new event * @structure: (transfer full): the structure for the event. The event will * take ownership of the structure. * * Create a new custom-typed event. This can be used for anything not * handled by other event-specific functions to pass an event to another * element. * * Make sure to allocate an event type with the #GST_EVENT_MAKE_TYPE macro, * assigning a free number and filling in the correct direction and * serialization flags. * * New custom events can also be created by subclassing the event type if * needed. * * Returns: (transfer full): the new custom event. */ GstEvent * gst_event_new_custom (GstEventType type, GstStructure * structure) { GstEventImpl *event; event = g_slice_new0 (GstEventImpl); GST_CAT_DEBUG (GST_CAT_EVENT, "creating new event %p %s %d", event, gst_event_type_get_name (type), type); if (structure) { /* structure must not have a parent */ if (!gst_structure_set_parent_refcount (structure, &event->event.mini_object.refcount)) goto had_parent; } gst_event_init (event, type); GST_EVENT_STRUCTURE (event) = structure; return GST_EVENT_CAST (event); /* ERRORS */ had_parent: { g_slice_free1 (sizeof (GstEventImpl), event); g_warning ("structure is already owned by another object"); return NULL; } }
static gboolean gst_fake_src_event_handler (GstBaseSrc * basesrc, GstEvent * event) { GstFakeSrc *src; src = GST_FAKE_SRC (basesrc); if (!src->silent) { const GstStructure *s; const gchar *tstr; gchar *sstr; GST_OBJECT_LOCK (src); g_free (src->last_message); tstr = gst_event_type_get_name (GST_EVENT_TYPE (event)); if ((s = gst_event_get_structure (event))) sstr = gst_structure_to_string (s); else sstr = g_strdup (""); src->last_message = g_strdup_printf ("event ******* (%s:%s) E (type: %s (%d), %s) %p", GST_DEBUG_PAD_NAME (GST_BASE_SRC_CAST (src)->srcpad), tstr, GST_EVENT_TYPE (event), sstr, event); g_free (sstr); GST_OBJECT_UNLOCK (src); g_object_notify_by_pspec ((GObject *) src, pspec_last_message); } return GST_BASE_SRC_CLASS (parent_class)->event (basesrc, event); }
static gboolean pad_event_handler(GstPad *pad, GstEvent *event) { // Establish thread-local. MpfComponent *component = MPF_COMPONENT(GST_OBJECT_PARENT(pad)); mpf_component_set_curcomponent(component); GstElement *element = gst_pad_get_parent_element(pad); gchar *elementname = gst_element_get_name(element); gchar *padname = gst_pad_get_name(pad); const gchar *eventname = gst_event_type_get_name(event->type); MPF_PRIVATE_ALWAYS("element=%s pad=%s event=%s\n", elementname, padname, eventname); // If EOS, poke a message out of the events pad. if (event->type == GST_EVENT_EOS) { GstPad *events = gst_element_get_pad(element, "events"); printf("GstPad *events=%p\n", events); GString *string = g_string_new(""); g_string_printf(string, "%s: EOS buffer_count=%d\n", elementname, mpf_private.buffer_count); mpf_voidstar_push("events", mpf_voidstar_stralloc(string->str)); mpf_voidstar_send_outbuffers(); gst_pad_push_event(events, gst_event_new_eos()); } g_free(elementname); g_free(padname); return gst_pad_event_default(pad, event); }
static GstPadProbeReturn accept_eos_probe (GstPad * pad, GstPadProbeInfo * info, gpointer data) { GstEvent *event = gst_pad_probe_info_get_event (info); GstEventType type = GST_EVENT_TYPE (event); if (type == GST_EVENT_EOS || type == GST_EVENT_FLUSH_START || type == GST_EVENT_FLUSH_STOP) { KmsElement *self; gboolean accept; self = KMS_ELEMENT (data); KMS_ELEMENT_LOCK (self); accept = self->priv->accept_eos; KMS_ELEMENT_UNLOCK (self); if (!accept) { GST_DEBUG_OBJECT (pad, "Event %s dropped", gst_event_type_get_name (type)); } return (accept) ? GST_PAD_PROBE_OK : GST_PAD_PROBE_DROP; } return GST_PAD_PROBE_OK; }
static gboolean event_func (GstPad * pad, GstEvent * event) { GST_DEBUG ("%s event", gst_event_type_get_name (GST_EVENT_TYPE (event))); events = g_list_append (events, event); return TRUE; }
static gboolean handle_queued_objects (APP_STATE_T * state) { GstMiniObject *object = NULL; g_mutex_lock (&state->queue_lock); if (state->flushing) { g_cond_broadcast (&state->cond); goto beach; } else if (g_async_queue_length (state->queue) == 0) { goto beach; } if ((object = g_async_queue_try_pop (state->queue))) { if (GST_IS_BUFFER (object)) { GstBuffer *buffer = GST_BUFFER_CAST (object); update_image (state, buffer); render_scene (state); gst_buffer_unref (buffer); if (!SYNC_BUFFERS) { object = NULL; } } else if (GST_IS_QUERY (object)) { GstQuery *query = GST_QUERY_CAST (object); GstStructure *s = (GstStructure *) gst_query_get_structure (query); if (gst_structure_has_name (s, "not-used")) { g_assert_not_reached (); } else { g_assert_not_reached (); } } else if (GST_IS_EVENT (object)) { GstEvent *event = GST_EVENT_CAST (object); g_print ("\nevent %p %s\n", event, gst_event_type_get_name (GST_EVENT_TYPE (event))); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_EOS: flush_internal (state); break; default: break; } gst_event_unref (event); object = NULL; } } if (object) { state->popped_obj = object; g_cond_broadcast (&state->cond); } beach: g_mutex_unlock (&state->queue_lock); return FALSE; }
static gboolean mpegts_base_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) { gboolean res = TRUE; gboolean hard; MpegTSBase *base = GST_MPEGTS_BASE (parent); GST_DEBUG_OBJECT (base, "Got event %s", gst_event_type_get_name (GST_EVENT_TYPE (event))); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEGMENT: gst_event_copy_segment (event, &base->segment); GST_DEBUG_OBJECT (base, "Received segment %" GST_SEGMENT_FORMAT, &base->segment); /* Check if we need to switch PCR/PTS handling */ if (base->segment.format == GST_FORMAT_TIME) { base->packetizer->calculate_offset = FALSE; base->packetizer->calculate_skew = TRUE; } else { base->packetizer->calculate_offset = TRUE; base->packetizer->calculate_skew = FALSE; } res = GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, event); break; case GST_EVENT_STREAM_START: gst_event_unref (event); break; case GST_EVENT_EOS: res = GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, event); res = gst_mpegts_base_handle_eos (base); break; case GST_EVENT_CAPS: /* FIXME, do something */ gst_event_unref (event); break; case GST_EVENT_FLUSH_STOP: res = GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, event); hard = (base->mode != BASE_MODE_SEEKING); mpegts_packetizer_flush (base->packetizer, hard); mpegts_base_flush (base, hard); gst_segment_init (&base->segment, GST_FORMAT_UNDEFINED); base->seen_pat = FALSE; break; default: res = GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, event); } /* Always return TRUE for sticky events */ if (GST_EVENT_IS_STICKY (event)) res = TRUE; return res; }
static gboolean gst_fake_sink_event (GstBaseSink * bsink, GstEvent * event) { GstFakeSink *sink = GST_FAKE_SINK (bsink); if (!sink->silent) { const GstStructure *s; const gchar *tstr; gchar *sstr; GST_OBJECT_LOCK (sink); g_free (sink->last_message); if (GST_EVENT_TYPE (event) == GST_EVENT_SINK_MESSAGE) { GstMessage *msg; const GstStructure *structure; gst_event_parse_sink_message (event, &msg); structure = gst_message_get_structure (msg); sstr = gst_structure_to_string (structure); sink->last_message = g_strdup_printf ("message ******* (%s:%s) M (type: %d, %s) %p", GST_DEBUG_PAD_NAME (GST_BASE_SINK_CAST (sink)->sinkpad), GST_MESSAGE_TYPE (msg), sstr, msg); gst_message_unref (msg); } else { tstr = gst_event_type_get_name (GST_EVENT_TYPE (event)); if ((s = gst_event_get_structure (event))) { sstr = gst_structure_to_string (s); } else { sstr = g_strdup (""); } sink->last_message = g_strdup_printf ("event ******* (%s:%s) E (type: %s (%d), %s) %p", GST_DEBUG_PAD_NAME (GST_BASE_SINK_CAST (sink)->sinkpad), tstr, GST_EVENT_TYPE (event), sstr, event); } g_free (sstr); GST_OBJECT_UNLOCK (sink); gst_fake_sink_notify_last_message (sink); } return GST_BASE_SINK_CLASS (parent_class)->event (bsink, event); }
static GstEvent * gst_event_new (GstEventType type) { GstEvent *event; event = (GstEvent *) gst_mini_object_new (GST_TYPE_EVENT); GST_CAT_DEBUG (GST_CAT_EVENT, "creating new event %p %s %d", event, gst_event_type_get_name (type), type); event->type = type; event->src = NULL; event->structure = NULL; GST_EVENT_SEQNUM (event) = gst_util_seqnum_next (); return event; }
static gboolean gst_teletextdec_sink_event (GstPad * pad, GstEvent * event) { gboolean ret; GstTeletextDec *teletext = GST_TELETEXTDEC (gst_pad_get_parent (pad)); GST_DEBUG_OBJECT (teletext, "got event %s", gst_event_type_get_name (GST_EVENT_TYPE (event))); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_NEWSEGMENT: /* maybe save and/or update the current segment (e.g. for output * clipping) or convert the event into one in a different format * (e.g. BYTES to TIME) or drop it and set a flag to send a newsegment * event in a different format later */ ret = gst_pad_push_event (teletext->srcpad, event); break; case GST_EVENT_EOS: /* end-of-stream, we should close down all stream leftovers here */ gst_teletextdec_zvbi_clear (teletext); ret = gst_pad_push_event (teletext->srcpad, event); break; case GST_EVENT_FLUSH_STOP: gst_teletextdec_zvbi_clear (teletext); gst_teletextdec_zvbi_init (teletext); ret = gst_pad_push_event (teletext->srcpad, event); break; default: ret = gst_pad_event_default (pad, event); break; } gst_object_unref (teletext); return ret; }
static gboolean gst_identity_sink_event (GstBaseTransform * trans, GstEvent * event) { GstIdentity *identity; gboolean ret = TRUE; identity = GST_IDENTITY (trans); if (!identity->silent) { const GstStructure *s; const gchar *tstr; gchar *sstr; GST_OBJECT_LOCK (identity); g_free (identity->last_message); tstr = gst_event_type_get_name (GST_EVENT_TYPE (event)); if ((s = gst_event_get_structure (event))) sstr = gst_structure_to_string (s); else sstr = g_strdup (""); identity->last_message = g_strdup_printf ("event ******* (%s:%s) E (type: %s (%d), %s) %p", GST_DEBUG_PAD_NAME (trans->sinkpad), tstr, GST_EVENT_TYPE (event), sstr, event); g_free (sstr); GST_OBJECT_UNLOCK (identity); gst_identity_notify_last_message (identity); } if (identity->single_segment && (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT)) { if (trans->have_segment == FALSE) { GstEvent *news; GstSegment segment; gst_event_copy_segment (event, &segment); gst_event_copy_segment (event, &trans->segment); trans->have_segment = TRUE; /* This is the first segment, send out a (0, -1) segment */ gst_segment_init (&segment, segment.format); news = gst_event_new_segment (&segment); gst_pad_event_default (trans->sinkpad, GST_OBJECT_CAST (trans), news); } } /* Reset previous timestamp, duration and offsets on NEWSEGMENT * to prevent false warnings when checking for perfect streams */ if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) { identity->prev_timestamp = identity->prev_duration = GST_CLOCK_TIME_NONE; identity->prev_offset = identity->prev_offset_end = GST_BUFFER_OFFSET_NONE; } if (identity->single_segment && GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) { /* eat up segments */ gst_event_unref (event); ret = TRUE; } else { if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_START) { GST_OBJECT_LOCK (identity); if (identity->clock_id) { GST_DEBUG_OBJECT (identity, "unlock clock wait"); gst_clock_id_unschedule (identity->clock_id); gst_clock_id_unref (identity->clock_id); identity->clock_id = NULL; } GST_OBJECT_UNLOCK (identity); } ret = GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (trans, event); } return ret; }
static gboolean handle_queued_objects (APP_STATE_T * state) { GstMiniObject *object = NULL; g_mutex_lock (state->queue_lock); if (state->flushing) { g_cond_broadcast (state->cond); goto beach; } else if (g_async_queue_length (state->queue) == 0) { goto beach; } if ((object = g_async_queue_try_pop (state->queue))) { if (GST_IS_BUFFER (object)) { GstBuffer *buffer = GST_BUFFER_CAST (object); update_image (state, buffer); render_scene (state); gst_buffer_unref (buffer); if (!SYNC_BUFFERS) { object = NULL; } } else if (GST_IS_QUERY (object)) { GstQuery *query = GST_QUERY_CAST (object); GstStructure *s = (GstStructure *) gst_query_get_structure (query); if (gst_structure_has_name (s, "eglglessink-allocate-eglimage")) { GstBuffer *buffer; GstVideoFormat format; gint width, height; GValue v = { 0, }; if (!gst_structure_get_enum (s, "format", GST_TYPE_VIDEO_FORMAT, (gint *) & format) || !gst_structure_get_int (s, "width", &width) || !gst_structure_get_int (s, "height", &height)) { g_assert_not_reached (); } buffer = gst_egl_allocate_eglimage (state, GST_EGL_IMAGE_BUFFER_POOL (state->pool)->allocator, format, width, height); g_value_init (&v, G_TYPE_POINTER); g_value_set_pointer (&v, buffer); gst_structure_set_value (s, "buffer", &v); g_value_unset (&v); } else { g_assert_not_reached (); } } else if (GST_IS_EVENT (object)) { GstEvent *event = GST_EVENT_CAST (object); g_print ("\nevent %p %s\n", event, gst_event_type_get_name (GST_EVENT_TYPE (event))); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_EOS: flush_internal (state); break; default: break; } gst_event_unref (event); object = NULL; } } if (object) { state->popped_obj = object; g_cond_broadcast (state->cond); } beach: g_mutex_unlock (state->queue_lock); return FALSE; }
static GstFlowReturn gst_gdp_depay_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) { GstGDPDepay *this; GstFlowReturn ret = GST_FLOW_OK; GstCaps *caps; GstBuffer *buf; GstEvent *event; guint available; this = GST_GDP_DEPAY (parent); /* On DISCONT, get rid of accumulated data. We assume a buffer after the * DISCONT contains (part of) a new valid header, if not we error because we * lost sync */ if (GST_BUFFER_IS_DISCONT (buffer)) { gst_adapter_clear (this->adapter); this->state = GST_GDP_DEPAY_STATE_HEADER; } gst_adapter_push (this->adapter, buffer); while (TRUE) { switch (this->state) { case GST_GDP_DEPAY_STATE_HEADER: { guint8 *header; /* collect a complete header, validate and store the header. Figure out * the payload length and switch to the PAYLOAD state */ available = gst_adapter_available (this->adapter); if (available < GST_DP_HEADER_LENGTH) goto done; GST_LOG_OBJECT (this, "reading GDP header from adapter"); header = gst_adapter_take (this->adapter, GST_DP_HEADER_LENGTH); if (!gst_dp_validate_header (GST_DP_HEADER_LENGTH, header)) { g_free (header); goto header_validate_error; } /* store types and payload length. Also store the header, which we need * to make the payload. */ this->payload_length = gst_dp_header_payload_length (header); this->payload_type = gst_dp_header_payload_type (header); /* free previous header and store new one. */ g_free (this->header); this->header = header; GST_LOG_OBJECT (this, "read GDP header, payload size %d, payload type %d, switching to state PAYLOAD", this->payload_length, this->payload_type); this->state = GST_GDP_DEPAY_STATE_PAYLOAD; break; } case GST_GDP_DEPAY_STATE_PAYLOAD: { /* in this state we wait for all the payload data to be available in the * adapter. Then we switch to the state where we actually process the * payload. */ available = gst_adapter_available (this->adapter); if (available < this->payload_length) goto done; /* change state based on type */ if (this->payload_type == GST_DP_PAYLOAD_BUFFER) { GST_LOG_OBJECT (this, "switching to state BUFFER"); this->state = GST_GDP_DEPAY_STATE_BUFFER; } else if (this->payload_type == GST_DP_PAYLOAD_CAPS) { GST_LOG_OBJECT (this, "switching to state CAPS"); this->state = GST_GDP_DEPAY_STATE_CAPS; } else if (this->payload_type >= GST_DP_PAYLOAD_EVENT_NONE) { GST_LOG_OBJECT (this, "switching to state EVENT"); this->state = GST_GDP_DEPAY_STATE_EVENT; } else { goto wrong_type; } if (this->payload_length) { const guint8 *data; gboolean res; data = gst_adapter_map (this->adapter, this->payload_length); res = gst_dp_validate_payload (GST_DP_HEADER_LENGTH, this->header, data); gst_adapter_unmap (this->adapter); if (!res) goto payload_validate_error; } break; } case GST_GDP_DEPAY_STATE_BUFFER: { /* if we receive a buffer without caps first, we error out */ if (!this->caps) goto no_caps; GST_LOG_OBJECT (this, "reading GDP buffer from adapter"); buf = gst_dp_buffer_from_header (GST_DP_HEADER_LENGTH, this->header); if (!buf) goto buffer_failed; /* now take the payload if there is any */ if (this->payload_length > 0) { GstMapInfo map; gst_buffer_map (buf, &map, GST_MAP_WRITE); gst_adapter_copy (this->adapter, map.data, 0, this->payload_length); gst_buffer_unmap (buf, &map); gst_adapter_flush (this->adapter, this->payload_length); } /* set caps and push */ GST_LOG_OBJECT (this, "deserialized buffer %p, pushing, timestamp %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT ", offset %" G_GINT64_FORMAT ", offset_end %" G_GINT64_FORMAT ", size %" G_GSIZE_FORMAT ", flags 0x%x", buf, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_BUFFER_OFFSET (buf), GST_BUFFER_OFFSET_END (buf), gst_buffer_get_size (buf), GST_BUFFER_FLAGS (buf)); ret = gst_pad_push (this->srcpad, buf); if (ret != GST_FLOW_OK) goto push_error; GST_LOG_OBJECT (this, "switching to state HEADER"); this->state = GST_GDP_DEPAY_STATE_HEADER; break; } case GST_GDP_DEPAY_STATE_CAPS: { guint8 *payload; /* take the payload of the caps */ GST_LOG_OBJECT (this, "reading GDP caps from adapter"); payload = gst_adapter_take (this->adapter, this->payload_length); caps = gst_dp_caps_from_packet (GST_DP_HEADER_LENGTH, this->header, payload); g_free (payload); if (!caps) goto caps_failed; GST_DEBUG_OBJECT (this, "deserialized caps %" GST_PTR_FORMAT, caps); gst_caps_replace (&(this->caps), caps); gst_pad_set_caps (this->srcpad, caps); /* drop the creation ref we still have */ gst_caps_unref (caps); GST_LOG_OBJECT (this, "switching to state HEADER"); this->state = GST_GDP_DEPAY_STATE_HEADER; break; } case GST_GDP_DEPAY_STATE_EVENT: { guint8 *payload; GST_LOG_OBJECT (this, "reading GDP event from adapter"); /* adapter doesn't like 0 length payload */ if (this->payload_length > 0) payload = gst_adapter_take (this->adapter, this->payload_length); else payload = NULL; event = gst_dp_event_from_packet (GST_DP_HEADER_LENGTH, this->header, payload); g_free (payload); if (!event) goto event_failed; GST_DEBUG_OBJECT (this, "deserialized event %p of type %s, pushing", event, gst_event_type_get_name (event->type)); gst_pad_push_event (this->srcpad, event); GST_LOG_OBJECT (this, "switching to state HEADER"); this->state = GST_GDP_DEPAY_STATE_HEADER; break; } } } done: return ret; /* ERRORS */ header_validate_error: { GST_ELEMENT_ERROR (this, STREAM, DECODE, (NULL), ("GDP packet header does not validate")); ret = GST_FLOW_ERROR; goto done; } payload_validate_error: { GST_ELEMENT_ERROR (this, STREAM, DECODE, (NULL), ("GDP packet payload does not validate")); ret = GST_FLOW_ERROR; goto done; } wrong_type: { GST_ELEMENT_ERROR (this, STREAM, DECODE, (NULL), ("GDP packet header is of wrong type")); ret = GST_FLOW_ERROR; goto done; } no_caps: { GST_ELEMENT_ERROR (this, STREAM, DECODE, (NULL), ("Received a buffer without first receiving caps")); ret = GST_FLOW_NOT_NEGOTIATED; goto done; } buffer_failed: { GST_ELEMENT_ERROR (this, STREAM, DECODE, (NULL), ("could not create buffer from GDP packet")); ret = GST_FLOW_ERROR; goto done; } push_error: { GST_WARNING_OBJECT (this, "pushing depayloaded buffer returned %d", ret); goto done; } caps_failed: { GST_ELEMENT_ERROR (this, STREAM, DECODE, (NULL), ("could not create caps from GDP packet")); ret = GST_FLOW_ERROR; goto done; } event_failed: { GST_ELEMENT_ERROR (this, STREAM, DECODE, (NULL), ("could not create event from GDP packet")); ret = GST_FLOW_ERROR; goto done; } }
static gboolean gst_dvbvideosink_event(GstBaseSink *sink, GstEvent *event) { GstDVBVideoSink *self = GST_DVBVIDEOSINK (sink); GST_DEBUG_OBJECT (self, "EVENT %s", gst_event_type_get_name(GST_EVENT_TYPE (event))); int ret = TRUE; switch (GST_EVENT_TYPE (event)) { case GST_EVENT_FLUSH_START: self->flushing = TRUE; /* wakeup the poll */ write(self->unlockfd[1], "\x01", 1); break; case GST_EVENT_FLUSH_STOP: if (self->fd >= 0) ioctl(self->fd, VIDEO_CLEAR_BUFFER); GST_OBJECT_LOCK(self); self->must_send_header = TRUE; while (self->queue) { queue_pop(&self->queue); } self->flushing = FALSE; GST_OBJECT_UNLOCK(self); break; case GST_EVENT_EOS: { struct pollfd pfd[2]; pfd[0].fd = self->unlockfd[0]; pfd[0].events = POLLIN; pfd[1].fd = self->fd; pfd[1].events = POLLIN; GST_PAD_PREROLL_UNLOCK(sink->sinkpad); while (1) { int retval = poll(pfd, 2, 250); if (retval < 0) { perror("poll in EVENT_EOS"); ret = FALSE; break; } if (pfd[0].revents & POLLIN) { GST_DEBUG_OBJECT (self, "wait EOS aborted!!\n"); ret = FALSE; break; } if (pfd[1].revents & POLLIN) { GST_DEBUG_OBJECT (self, "got buffer empty from driver!\n"); break; } if (sink->flushing) { GST_DEBUG_OBJECT (self, "wait EOS flushing!!\n"); ret = FALSE; break; } } GST_PAD_PREROLL_LOCK(sink->sinkpad); break; } case GST_EVENT_NEWSEGMENT: { GstFormat format; gboolean update; gdouble rate; gint64 start, end, pos; int skip = 0, repeat = 0; gst_event_parse_new_segment(event, &update, &rate, &format, &start, &end, &pos); GST_DEBUG_OBJECT(self, "GST_EVENT_NEWSEGMENT rate=%f\n", rate); if (format == GST_FORMAT_TIME) { self->timestamp_offset = start - pos; if (rate != self->rate) { if (rate > 1.0) { skip = (int)rate; } else if (rate < 1.0) { repeat = 1.0 / rate; } ioctl(self->fd, VIDEO_SLOWMOTION, repeat); ioctl(self->fd, VIDEO_FAST_FORWARD, skip); self->rate = rate; } } break; } default: break; } return ret; }