static gboolean gst_droid_cam_src_vfsrc_activatepush (GstPad * pad, gboolean active) { GstDroidCamSrc *src; src = GST_DROID_CAM_SRC (GST_OBJECT_PARENT (pad)); GST_DEBUG_OBJECT (src, "vfsrc activatepush: %d", active); if (active) { gboolean started; src->send_new_segment = TRUE; /* First we do caps negotiation */ if (!gst_droid_cam_src_vfsrc_negotiate (src)) { return FALSE; } /* Then we start our task */ GST_PAD_STREAM_LOCK (pad); started = gst_vf_src_pad_start_task (pad); if (!started) { GST_CAMERA_BUFFER_POOL_LOCK (src->pool); src->pool->flushing = TRUE; GST_CAMERA_BUFFER_POOL_UNLOCK (src->pool); GST_PAD_STREAM_UNLOCK (pad); GST_ERROR_OBJECT (src, "Failed to start task"); gst_pad_stop_task (pad); return FALSE; } GST_CAMERA_BUFFER_POOL_LOCK (src->pool); src->pool->flushing = FALSE; GST_CAMERA_BUFFER_POOL_UNLOCK (src->pool); GST_PAD_STREAM_UNLOCK (pad); GST_DEBUG_OBJECT (src, "task started"); } else { GST_CAMERA_BUFFER_POOL_LOCK (src->pool); src->pool->flushing = TRUE; GST_CAMERA_BUFFER_POOL_UNLOCK (src->pool); GST_DEBUG_OBJECT (src, "stopping task"); gst_camera_buffer_pool_unlock_app_queue (src->pool); gst_pad_stop_task (pad); GST_DEBUG_OBJECT (src, "stopped task"); } return TRUE; }
static void gst_omx_audio_enc_flush (GstAudioEncoder * encoder) { GstOMXAudioEnc *self; self = GST_OMX_AUDIO_ENC (encoder); GST_DEBUG_OBJECT (self, "Resetting encoder"); gst_omx_audio_enc_drain (self); gst_omx_port_set_flushing (self->enc_in_port, 5 * GST_SECOND, TRUE); gst_omx_port_set_flushing (self->enc_out_port, 5 * GST_SECOND, TRUE); /* Wait until the srcpad loop is finished */ GST_AUDIO_ENCODER_STREAM_UNLOCK (self); GST_PAD_STREAM_LOCK (GST_AUDIO_ENCODER_SRC_PAD (self)); GST_PAD_STREAM_UNLOCK (GST_AUDIO_ENCODER_SRC_PAD (self)); GST_AUDIO_ENCODER_STREAM_LOCK (self); gst_omx_port_set_flushing (self->enc_in_port, 5 * GST_SECOND, FALSE); gst_omx_port_set_flushing (self->enc_out_port, 5 * GST_SECOND, FALSE); gst_omx_port_populate (self->enc_out_port); /* Start the srcpad loop again */ self->last_upstream_ts = 0; self->downstream_flow_ret = GST_FLOW_OK; self->eos = FALSE; gst_pad_start_task (GST_AUDIO_ENCODER_SRC_PAD (self), (GstTaskFunction) gst_omx_audio_enc_loop, encoder, NULL); }
static void gst_amc_audio_dec_flush (GstAudioDecoder * decoder, gboolean hard) { GstAmcAudioDec *self; self = GST_AMC_AUDIO_DEC (decoder); GST_DEBUG_OBJECT (self, "Resetting decoder"); if (!self->started) { GST_DEBUG_OBJECT (self, "Codec not started yet"); return; } self->flushing = TRUE; gst_amc_codec_flush (self->codec); /* Wait until the srcpad loop is finished, * unlock GST_AUDIO_DECODER_STREAM_LOCK to prevent deadlocks * caused by using this lock from inside the loop function */ GST_AUDIO_DECODER_STREAM_UNLOCK (self); GST_PAD_STREAM_LOCK (GST_AUDIO_DECODER_SRC_PAD (self)); GST_PAD_STREAM_UNLOCK (GST_AUDIO_DECODER_SRC_PAD (self)); GST_AUDIO_DECODER_STREAM_LOCK (self); self->flushing = FALSE; /* Start the srcpad loop again */ self->last_upstream_ts = 0; self->eos = FALSE; self->downstream_flow_ret = GST_FLOW_OK; gst_pad_start_task (GST_AUDIO_DECODER_SRC_PAD (self), (GstTaskFunction) gst_amc_audio_dec_loop, decoder, NULL); GST_DEBUG_OBJECT (self, "Reset decoder"); }
static gboolean gst_base_video_decoder_sink_activate (GstBaseVideoDecoder * decoder, gboolean active) { GstBaseVideoDecoderClass *klass; gboolean result = FALSE; GST_DEBUG_OBJECT (decoder, "activate"); klass = GST_BASE_VIDEO_DECODER_GET_CLASS (decoder); if (active) { if (klass->start) result = klass->start (decoder); } else { /* We must make sure streaming has finished before resetting things * and calling the ::stop vfunc */ GST_PAD_STREAM_LOCK (GST_BASE_VIDEO_CODEC_SINK_PAD (decoder)); GST_PAD_STREAM_UNLOCK (GST_BASE_VIDEO_CODEC_SINK_PAD (decoder)); if (klass->stop) result = klass->stop (decoder); } GST_DEBUG_OBJECT (decoder, "activate: %d", result); return result; }
static gboolean gst_cdxa_parse_do_seek (GstCDXAParse * cdxa, GstEvent * event) { GstSeekFlags flags; GstSeekType start_type; GstFormat format; gint64 start, off, upstream_size; gst_event_parse_seek (event, NULL, &format, &flags, &start_type, &start, NULL, NULL); if (format != GST_FORMAT_BYTES) { GST_DEBUG_OBJECT (cdxa, "Can only handle seek in BYTES format"); return FALSE; } if (start_type != GST_SEEK_TYPE_SET) { GST_DEBUG_OBJECT (cdxa, "Can only handle seek from start (SEEK_TYPE_SET)"); return FALSE; } GST_OBJECT_LOCK (cdxa); off = gst_cdxa_parse_convert_src_to_sink_offset (cdxa, start); upstream_size = cdxa->datasize; GST_OBJECT_UNLOCK (cdxa); if (off >= upstream_size) { GST_DEBUG_OBJECT (cdxa, "Invalid target offset %" G_GINT64_FORMAT ", file " "is only %" G_GINT64_FORMAT " bytes in size", off, upstream_size); return FALSE; } /* unlock upstream pull_range */ gst_pad_push_event (cdxa->sinkpad, gst_event_new_flush_start ()); /* make sure our loop function exits */ gst_pad_push_event (cdxa->srcpad, gst_event_new_flush_start ()); /* wait for streaming to finish */ GST_PAD_STREAM_LOCK (cdxa->sinkpad); /* prepare for streaming again */ gst_pad_push_event (cdxa->sinkpad, gst_event_new_flush_stop ()); gst_pad_push_event (cdxa->srcpad, gst_event_new_flush_stop ()); gst_pad_push_event (cdxa->srcpad, gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, start, GST_CLOCK_TIME_NONE, 0)); GST_OBJECT_LOCK (cdxa); cdxa->offset = off; GST_OBJECT_UNLOCK (cdxa); /* and restart */ gst_pad_start_task (cdxa->sinkpad, (GstTaskFunction) gst_cdxa_parse_loop, cdxa->sinkpad, NULL); GST_PAD_STREAM_UNLOCK (cdxa->sinkpad); return TRUE; }
static GstFlowReturn gst_funnel_sink_chain_object (GstPad * pad, GstFunnel * funnel, gboolean is_list, GstMiniObject * obj) { GstFlowReturn res; GST_DEBUG_OBJECT (pad, "received %" GST_PTR_FORMAT, obj); GST_PAD_STREAM_LOCK (funnel->srcpad); if ((funnel->last_sinkpad == NULL) || ((funnel->forward_sticky_events_mode != GST_FUNNEL_FORWARD_STICKY_EVENTS_MODE_NEVER) && (funnel->last_sinkpad != pad))) { GST_DEBUG_OBJECT (pad, "Forwarding sticky events"); gst_pad_sticky_events_foreach (pad, forward_events_on_stream_changed, funnel); gst_object_replace ((GstObject **) & funnel->last_sinkpad, GST_OBJECT (pad)); } if (is_list) res = gst_pad_push_list (funnel->srcpad, GST_BUFFER_LIST_CAST (obj)); else res = gst_pad_push (funnel->srcpad, GST_BUFFER_CAST (obj)); GST_PAD_STREAM_UNLOCK (funnel->srcpad); GST_LOG_OBJECT (pad, "handled buffer%s %s", (is_list ? "list" : ""), gst_flow_get_name (res)); return res; }
static gboolean gst_wavpack_parse_sink_event (GstPad * pad, GstEvent * event) { GstWavpackParse *parse; gboolean ret = TRUE; parse = GST_WAVPACK_PARSE (gst_pad_get_parent (pad)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_FLUSH_STOP:{ if (parse->adapter) { gst_adapter_clear (parse->adapter); } if (parse->pending_buffer) { gst_buffer_unref (parse->pending_buffer); parse->pending_buffer = NULL; parse->pending_offset = 0; } ret = gst_pad_push_event (parse->srcpad, event); break; } case GST_EVENT_NEWSEGMENT:{ parse->need_newsegment = TRUE; gst_event_unref (event); ret = TRUE; break; } case GST_EVENT_EOS:{ if (parse->adapter) { /* remove all bytes that are left in the adapter after EOS. They can't * be a complete Wavpack block and we can't do anything with them */ gst_adapter_clear (parse->adapter); } if (parse->pending_buffer) { gst_buffer_unref (parse->pending_buffer); parse->pending_buffer = NULL; parse->pending_offset = 0; } ret = gst_pad_push_event (parse->srcpad, event); break; } default:{ /* stream lock is recursive, should be fine for all events */ GST_PAD_STREAM_LOCK (pad); if (parse->srcpad == NULL) { parse->queued_events = g_list_append (parse->queued_events, event); } else { ret = gst_pad_push_event (parse->srcpad, event); } GST_PAD_STREAM_UNLOCK (pad); } } gst_object_unref (parse); return ret; }
static gboolean speex_dec_src_query (GstPad * pad, GstQuery * query) { GstSpeexDec *dec; gboolean res = FALSE; dec = GST_SPEEX_DEC (gst_pad_get_parent (pad)); switch (GST_QUERY_TYPE (query)) { case GST_QUERY_POSITION:{ GstSegment segment; GstFormat format; gint64 cur; gst_query_parse_position (query, &format, NULL); GST_PAD_STREAM_LOCK (dec->sinkpad); segment = dec->segment; GST_PAD_STREAM_UNLOCK (dec->sinkpad); if (segment.format != GST_FORMAT_TIME) { GST_DEBUG_OBJECT (dec, "segment not initialised yet"); break; } if ((res = speex_dec_convert (dec->srcpad, GST_FORMAT_TIME, segment.last_stop, &format, &cur))) { gst_query_set_position (query, format, cur); } break; } case GST_QUERY_DURATION:{ GstFormat format = GST_FORMAT_TIME; gint64 dur; /* get duration from demuxer */ if (!gst_pad_query_peer_duration (dec->sinkpad, &format, &dur)) break; gst_query_parse_duration (query, &format, NULL); /* and convert it into the requested format */ if ((res = speex_dec_convert (dec->srcpad, GST_FORMAT_TIME, dur, &format, &dur))) { gst_query_set_duration (query, format, dur); } break; } default: res = gst_pad_query_default (pad, query); break; } gst_object_unref (dec); return res; }
static gboolean gst_funnel_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) { GstFunnel *funnel = GST_FUNNEL (parent); GstFunnelPad *fpad = GST_FUNNEL_PAD_CAST (pad); gboolean forward = TRUE; gboolean res = TRUE; gboolean unlock = FALSE; GST_DEBUG_OBJECT (pad, "received event %" GST_PTR_FORMAT, event); if (GST_EVENT_IS_STICKY (event)) { unlock = TRUE; GST_PAD_STREAM_LOCK (funnel->srcpad); if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) { GST_OBJECT_LOCK (funnel); fpad->got_eos = TRUE; if (!gst_funnel_all_sinkpads_eos_unlocked (funnel, pad)) { forward = FALSE; } else { forward = TRUE; } GST_OBJECT_UNLOCK (funnel); } else if (pad != funnel->last_sinkpad) { forward = FALSE; } } else if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) { unlock = TRUE; GST_PAD_STREAM_LOCK (funnel->srcpad); GST_OBJECT_LOCK (funnel); fpad->got_eos = FALSE; GST_OBJECT_UNLOCK (funnel); } else if (GST_EVENT_TYPE (event) == GST_EVENT_GAP) { /* If no data is coming and we receive GAP event, need to forward sticky events. */ unlock = TRUE; GST_PAD_STREAM_LOCK (funnel->srcpad); GST_OBJECT_LOCK (funnel); gst_object_replace ((GstObject **) & funnel->last_sinkpad, GST_OBJECT (pad)); GST_OBJECT_UNLOCK (funnel); gst_pad_sticky_events_foreach (pad, forward_events, funnel->srcpad); } if (forward) res = gst_pad_push_event (funnel->srcpad, event); else gst_event_unref (event); if (unlock) GST_PAD_STREAM_UNLOCK (funnel->srcpad); return res; }
static gboolean gst_rnd_buffer_size_src_event (GstPad * pad, GstObject * parent, GstEvent * event) { GstRndBufferSize *self; GstSeekType start_type; GstSeekFlags flags; GstFormat format; gint64 start; if (GST_EVENT_TYPE (event) != GST_EVENT_SEEK) { GST_WARNING_OBJECT (pad, "dropping %s event", GST_EVENT_TYPE_NAME (event)); return FALSE; } self = GST_RND_BUFFER_SIZE (parent); gst_event_parse_seek (event, NULL, &format, &flags, &start_type, &start, NULL, NULL); if (format != GST_FORMAT_BYTES) { GST_WARNING_OBJECT (pad, "only BYTE format supported"); return FALSE; } if (start_type != GST_SEEK_TYPE_SET) { GST_WARNING_OBJECT (pad, "only SEEK_TYPE_SET supported"); return FALSE; } if ((flags & GST_SEEK_FLAG_FLUSH)) { gst_pad_push_event (self->srcpad, gst_event_new_flush_start ()); gst_pad_push_event (self->sinkpad, gst_event_new_flush_start ()); } else { gst_pad_pause_task (self->sinkpad); } GST_PAD_STREAM_LOCK (self->sinkpad); if ((flags & GST_SEEK_FLAG_FLUSH)) { gst_pad_push_event (self->srcpad, gst_event_new_flush_stop (TRUE)); gst_pad_push_event (self->sinkpad, gst_event_new_flush_stop (TRUE)); } GST_INFO_OBJECT (pad, "seeking to offset %" G_GINT64_FORMAT, start); self->offset = start; self->need_newsegment = TRUE; gst_pad_start_task (self->sinkpad, (GstTaskFunction) gst_rnd_buffer_size_loop, self); GST_PAD_STREAM_UNLOCK (self->sinkpad); return TRUE; }
/** call control(FLUSH), and then process() to pop out all buffers */ static gboolean codec_flush (GstDucatiVidDec * self, gboolean eos) { gint err; GST_DEBUG_OBJECT (self, "flush: eos=%d", eos); /* note: flush is synchronized against _chain() to avoid calling * the codec from multiple threads */ GST_PAD_STREAM_LOCK (self->sinkpad); if (G_UNLIKELY (self->first_in_buffer)) { return TRUE; } if (G_UNLIKELY (!self->codec)) { GST_WARNING_OBJECT (self, "no codec"); return TRUE; } err = VIDDEC3_control (self->codec, XDM_FLUSH, self->dynParams, self->status); if (err) { GST_ERROR_OBJECT (self, "failed XDM_FLUSH"); goto out; } self->inBufs->descs[0].bufSize.bytes = 0; self->inArgs->numBytes = 0; self->inArgs->inputID = 0; do { err = codec_process (self, eos, TRUE); } while (err != XDM_EFAIL); /* on a flush, it is normal (and not an error) for the last _process() call * to return an error.. */ err = XDM_EOK; out: GST_PAD_STREAM_UNLOCK (self->sinkpad); GST_DEBUG_OBJECT (self, "done"); return !err; }
static gboolean set_element_zoom (GstWrapperCameraBinSrc * self, gfloat zoom) { gboolean ret = FALSE; GstBaseCameraSrc *bcamsrc = GST_BASE_CAMERA_SRC (self); gint w2_crop = 0, h2_crop = 0; GstPad *pad_zoom_sink = NULL; gint left = self->base_crop_left; gint right = self->base_crop_right; gint top = self->base_crop_top; gint bottom = self->base_crop_bottom; if (self->src_zoom_crop) { /* Update capsfilters to apply the zoom */ GST_INFO_OBJECT (self, "zoom: %f, orig size: %dx%d", zoom, bcamsrc->width, bcamsrc->height); if (zoom != ZOOM_1X) { w2_crop = (bcamsrc->width - (gint) (bcamsrc->width * ZOOM_1X / zoom)) / 2; h2_crop = (bcamsrc->height - (gint) (bcamsrc->height * ZOOM_1X / zoom)) / 2; left += w2_crop; right += w2_crop; top += h2_crop; bottom += h2_crop; /* force number of pixels cropped from left to be even, to avoid slow code * path on videoscale */ left &= 0xFFFE; } pad_zoom_sink = gst_element_get_static_pad (self->src_zoom_crop, "sink"); GST_INFO_OBJECT (self, "sw cropping: left:%d, right:%d, top:%d, bottom:%d", left, right, top, bottom); GST_PAD_STREAM_LOCK (pad_zoom_sink); g_object_set (self->src_zoom_crop, "left", left, "right", right, "top", top, "bottom", bottom, NULL); GST_PAD_STREAM_UNLOCK (pad_zoom_sink); gst_object_unref (pad_zoom_sink); ret = TRUE; } return ret; }
static gboolean gst_amc_video_enc_flush (GstVideoEncoder * encoder) { GstAmcVideoEnc *self; GError *err = NULL; self = GST_AMC_VIDEO_ENC (encoder); GST_DEBUG_OBJECT (self, "Flushing encoder"); if (!self->started) { GST_DEBUG_OBJECT (self, "Codec not started yet"); return TRUE; } self->flushing = TRUE; gst_amc_codec_flush (self->codec, &err); if (err) GST_ELEMENT_WARNING_FROM_ERROR (self, err); /* Wait until the srcpad loop is finished, * unlock GST_VIDEO_ENCODER_STREAM_LOCK to prevent deadlocks * caused by using this lock from inside the loop function */ GST_VIDEO_ENCODER_STREAM_UNLOCK (self); GST_PAD_STREAM_LOCK (GST_VIDEO_ENCODER_SRC_PAD (self)); GST_PAD_STREAM_UNLOCK (GST_VIDEO_ENCODER_SRC_PAD (self)); GST_VIDEO_ENCODER_STREAM_LOCK (self); self->flushing = FALSE; /* Start the srcpad loop again */ self->last_upstream_ts = 0; self->drained = TRUE; self->downstream_flow_ret = GST_FLOW_OK; gst_pad_start_task (GST_VIDEO_ENCODER_SRC_PAD (self), (GstTaskFunction) gst_amc_video_enc_loop, encoder, NULL); GST_DEBUG_OBJECT (self, "Flush encoder"); return TRUE; }
static gboolean gst_dvd_spu_subpic_event (GstPad * pad, GstObject * parent, GstEvent * event) { GstDVDSpu *dvdspu = (GstDVDSpu *) parent; gboolean res = TRUE; /* Some events on the subpicture sink pad just get ignored, like * FLUSH_START */ switch (GST_EVENT_TYPE (event)) { case GST_EVENT_CAPS: { GstCaps *caps; gst_event_parse_caps (event, &caps); res = gst_dvd_spu_subpic_set_caps (pad, caps); gst_event_unref (event); break; } case GST_EVENT_CUSTOM_DOWNSTREAM: case GST_EVENT_CUSTOM_DOWNSTREAM_STICKY: case GST_EVENT_CUSTOM_DOWNSTREAM_OOB: { const GstStructure *structure = gst_event_get_structure (event); gboolean need_push; if (!gst_structure_has_name (structure, "application/x-gst-dvd")) { res = gst_pad_event_default (pad, parent, event); break; } DVD_SPU_LOCK (dvdspu); if (GST_EVENT_IS_SERIALIZED (event)) { SpuPacket *spu_packet = g_new0 (SpuPacket, 1); GST_DEBUG_OBJECT (dvdspu, "Enqueueing DVD event on subpicture pad for later"); spu_packet->event = event; g_queue_push_tail (dvdspu->pending_spus, spu_packet); } else { gst_dvd_spu_handle_dvd_event (dvdspu, event); } /* If the handle_dvd_event generated a pending frame, we * need to synchronise with the video pad's stream lock and push it. * This requires some dancing to preserve locking order and handle * flushes correctly */ need_push = (dvdspu->pending_frame != NULL); DVD_SPU_UNLOCK (dvdspu); if (need_push) { GstBuffer *to_push = NULL; gboolean flushing; GST_LOG_OBJECT (dvdspu, "Going for stream lock"); GST_PAD_STREAM_LOCK (dvdspu->videosinkpad); GST_LOG_OBJECT (dvdspu, "Got stream lock"); GST_OBJECT_LOCK (dvdspu->videosinkpad); flushing = GST_PAD_IS_FLUSHING (dvdspu->videosinkpad); GST_OBJECT_UNLOCK (dvdspu->videosinkpad); DVD_SPU_LOCK (dvdspu); if (dvdspu->pending_frame == NULL || flushing) { /* Got flushed while waiting for the stream lock */ DVD_SPU_UNLOCK (dvdspu); } else { to_push = dvdspu->pending_frame; dvdspu->pending_frame = NULL; DVD_SPU_UNLOCK (dvdspu); gst_pad_push (dvdspu->srcpad, to_push); } GST_LOG_OBJECT (dvdspu, "Dropping stream lock"); GST_PAD_STREAM_UNLOCK (dvdspu->videosinkpad); } break; } case GST_EVENT_SEGMENT: { GstSegment seg; gst_event_copy_segment (event, &seg); /* Only print updates if they have an end time (don't print start_time * updates */ GST_DEBUG_OBJECT (dvdspu, "subpic pad Segment: %" GST_SEGMENT_FORMAT, &seg); DVD_SPU_LOCK (dvdspu); dvdspu->subp_seg = seg; GST_LOG_OBJECT (dvdspu, "Subpicture segment now: %" GST_SEGMENT_FORMAT, &dvdspu->subp_seg); DVD_SPU_UNLOCK (dvdspu); gst_event_unref (event); break; } case GST_EVENT_GAP: { GstClockTime timestamp, duration; gst_event_parse_gap (event, ×tamp, &duration); if (GST_CLOCK_TIME_IS_VALID (duration)) timestamp += duration; DVD_SPU_LOCK (dvdspu); dvdspu->subp_seg.position = timestamp; GST_LOG_OBJECT (dvdspu, "Received GAP. Segment now: %" GST_SEGMENT_FORMAT, &dvdspu->subp_seg); DVD_SPU_UNLOCK (dvdspu); gst_event_unref (event); break; } case GST_EVENT_FLUSH_START: gst_event_unref (event); goto done; case GST_EVENT_FLUSH_STOP: GST_DEBUG_OBJECT (dvdspu, "Have flush-stop event on SPU pad"); DVD_SPU_LOCK (dvdspu); gst_segment_init (&dvdspu->subp_seg, GST_FORMAT_UNDEFINED); gst_dvd_spu_flush_spu_info (dvdspu, TRUE); DVD_SPU_UNLOCK (dvdspu); /* We don't forward flushes on the spu pad */ gst_event_unref (event); goto done; case GST_EVENT_EOS: /* drop EOS on the subtitle pad, it means there are no more subtitles, * video might still continue, though */ gst_event_unref (event); goto done; break; default: res = gst_pad_event_default (pad, parent, event); break; } done: return res; }
static gboolean gst_real_audio_demux_handle_seek (GstRealAudioDemux * demux, GstEvent * event) { GstFormat format; GstSeekFlags flags; GstSeekType cur_type, stop_type; gboolean flush, update; gdouble rate; guint64 seek_pos; gint64 cur, stop; if (!demux->seekable) goto not_seekable; if (demux->byterate_num == 0 || demux->byterate_denom == 0) goto no_bitrate; gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, &stop_type, &stop); if (format != GST_FORMAT_TIME) goto only_time_format_supported; if (rate <= 0.0) goto cannot_do_backwards_playback; flush = ((flags & GST_SEEK_FLAG_FLUSH) != 0); GST_DEBUG_OBJECT (demux, "flush=%d, rate=%g", flush, rate); /* unlock streaming thread and make streaming stop */ if (flush) { gst_pad_push_event (demux->sinkpad, gst_event_new_flush_start ()); gst_pad_push_event (demux->srcpad, gst_event_new_flush_start ()); } else { gst_pad_pause_task (demux->sinkpad); } GST_PAD_STREAM_LOCK (demux->sinkpad); if (demux->segment_running && !flush) { GstEvent *newseg; newseg = gst_event_new_new_segment_full (TRUE, demux->segment.rate, demux->segment.applied_rate, GST_FORMAT_TIME, demux->segment.start, demux->segment.last_stop, demux->segment.time); GST_DEBUG_OBJECT (demux, "sending NEWSEGMENT event to close the current " "segment: %" GST_PTR_FORMAT, newseg); gst_pad_push_event (demux->srcpad, newseg); } gst_segment_set_seek (&demux->segment, rate, format, flags, cur_type, cur, stop_type, stop, &update); GST_DEBUG_OBJECT (demux, "segment: %" GST_SEGMENT_FORMAT, &demux->segment); seek_pos = gst_util_uint64_scale (demux->segment.start, demux->byterate_num, demux->byterate_denom * GST_SECOND); if (demux->packet_size > 0) { seek_pos -= seek_pos % demux->packet_size; } seek_pos += demux->data_offset; GST_DEBUG_OBJECT (demux, "seek_pos = %" G_GUINT64_FORMAT, seek_pos); /* stop flushing */ gst_pad_push_event (demux->sinkpad, gst_event_new_flush_stop ()); gst_pad_push_event (demux->srcpad, gst_event_new_flush_stop ()); demux->offset = seek_pos; demux->need_newsegment = TRUE; /* notify start of new segment */ if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT) { gst_element_post_message (GST_ELEMENT (demux), gst_message_new_segment_start (GST_OBJECT (demux), GST_FORMAT_TIME, demux->segment.last_stop)); } demux->segment_running = TRUE; /* restart our task since it might have been stopped when we did the flush */ gst_pad_start_task (demux->sinkpad, (GstTaskFunction) gst_real_audio_demux_loop, demux); /* streaming can continue now */ GST_PAD_STREAM_UNLOCK (demux->sinkpad); return TRUE; /* ERRORS */ not_seekable: { GST_DEBUG_OBJECT (demux, "seek failed: cannot seek in streaming mode"); return FALSE; } no_bitrate: { GST_DEBUG_OBJECT (demux, "seek failed: bitrate unknown"); return FALSE; } only_time_format_supported: { GST_DEBUG_OBJECT (demux, "can only seek in TIME format"); return FALSE; } cannot_do_backwards_playback: { GST_DEBUG_OBJECT (demux, "can only seek with positive rate, not %lf", rate); return FALSE; } }
static gboolean gst_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; }
/* GstAggregator vmethods default implementations */ static gboolean _sink_event (GstAggregator * self, GstAggregatorPad * aggpad, GstEvent * event) { gboolean res = TRUE; GstPad *pad = GST_PAD (aggpad); GstAggregatorPrivate *priv = self->priv; GstAggregatorPadPrivate *padpriv = aggpad->priv; switch (GST_EVENT_TYPE (event)) { case GST_EVENT_FLUSH_START: { GstBuffer *tmpbuf; g_atomic_int_set (&aggpad->priv->flushing, TRUE); /* Remove pad buffer and wake up the streaming thread */ tmpbuf = gst_aggregator_pad_steal_buffer (aggpad); gst_buffer_replace (&tmpbuf, NULL); if (g_atomic_int_compare_and_exchange (&padpriv->pending_flush_start, TRUE, FALSE) == TRUE) { GST_DEBUG_OBJECT (aggpad, "Expecting FLUSH_STOP now"); g_atomic_int_set (&padpriv->pending_flush_stop, TRUE); } if (g_atomic_int_get (&priv->flush_seeking)) { /* If flush_seeking we forward the first FLUSH_START */ if (g_atomic_int_compare_and_exchange (&priv->pending_flush_start, TRUE, FALSE) == TRUE) { GST_DEBUG_OBJECT (self, "Flushing, pausing srcpad task"); _stop_srcpad_task (self, event); priv->flow_return = GST_FLOW_OK; GST_INFO_OBJECT (self, "Getting STREAM_LOCK while seeking"); GST_PAD_STREAM_LOCK (self->srcpad); GST_LOG_OBJECT (self, "GOT STREAM_LOCK"); event = NULL; goto eat; } } /* We forward only in one case: right after flush_seeking */ goto eat; } case GST_EVENT_FLUSH_STOP: { GST_DEBUG_OBJECT (aggpad, "Got FLUSH_STOP"); _aggpad_flush (aggpad, self); if (g_atomic_int_get (&priv->flush_seeking)) { g_atomic_int_set (&aggpad->priv->pending_flush_stop, FALSE); if (g_atomic_int_get (&priv->flush_seeking)) { if (_all_flush_stop_received (self)) { /* That means we received FLUSH_STOP/FLUSH_STOP on * all sinkpads -- Seeking is Done... sending FLUSH_STOP */ _flush (self); gst_pad_push_event (self->srcpad, event); priv->send_eos = TRUE; event = NULL; _add_aggregate_gsource (self); GST_INFO_OBJECT (self, "Releasing source pad STREAM_LOCK"); GST_PAD_STREAM_UNLOCK (self->srcpad); _start_srcpad_task (self); } } } /* We never forward the event */ goto eat; } case GST_EVENT_EOS: { GST_DEBUG_OBJECT (aggpad, "EOS"); /* We still have a buffer, and we don't want the subclass to have to * check for it. Mark pending_eos, eos will be set when steal_buffer is * called */ PAD_LOCK_EVENT (aggpad); if (!aggpad->buffer) { aggpad->eos = TRUE; } else { aggpad->priv->pending_eos = TRUE; } PAD_UNLOCK_EVENT (aggpad); _add_aggregate_gsource (self); goto eat; } case GST_EVENT_SEGMENT: { PAD_LOCK_EVENT (aggpad); gst_event_copy_segment (event, &aggpad->segment); PAD_UNLOCK_EVENT (aggpad); goto eat; } case GST_EVENT_STREAM_START: { goto eat; } case GST_EVENT_TAG: { GstTagList *tags; gst_event_parse_tag (event, &tags); if (gst_tag_list_get_scope (tags) == GST_TAG_SCOPE_STREAM) { gst_aggregator_merge_tags (self, tags, GST_TAG_MERGE_REPLACE); gst_event_unref (event); event = NULL; goto eat; } break; } default: { break; } } GST_DEBUG_OBJECT (pad, "Forwarding event: %" GST_PTR_FORMAT, event); return gst_pad_event_default (pad, GST_OBJECT (self), event); eat: GST_DEBUG_OBJECT (pad, "Eating event: %" GST_PTR_FORMAT, event); if (event) gst_event_unref (event); return res; }
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_timidity_src_event (GstPad * pad, GstEvent * event) { gboolean res = FALSE; GstTimidity *timidity = GST_TIMIDITY (gst_pad_get_parent (pad)); GST_DEBUG_OBJECT (pad, "%s event received", GST_EVENT_TYPE_NAME (event)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEEK: { gdouble rate; GstFormat src_format, dst_format; GstSeekFlags flags; GstSeekType start_type, stop_type; gint64 orig_start, start, stop; gboolean flush, update; if (!timidity->song) break; gst_event_parse_seek (event, &rate, &src_format, &flags, &start_type, &orig_start, &stop_type, &stop); dst_format = GST_FORMAT_DEFAULT; gst_timidity_src_convert (timidity, src_format, orig_start, &dst_format, &start); gst_timidity_src_convert (timidity, src_format, stop, &dst_format, &stop); flush = ((flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH); if (flush) { GST_DEBUG ("performing flush"); gst_pad_push_event (timidity->srcpad, gst_event_new_flush_start ()); } else { gst_pad_stop_task (timidity->sinkpad); } GST_PAD_STREAM_LOCK (timidity->sinkpad); if (flush) { gst_pad_push_event (timidity->srcpad, gst_event_new_flush_stop ()); } gst_segment_set_seek (timidity->o_segment, rate, dst_format, flags, start_type, start, stop_type, stop, &update); if ((flags && GST_SEEK_FLAG_SEGMENT) == GST_SEEK_FLAG_SEGMENT) { GST_DEBUG_OBJECT (timidity, "received segment seek %d, %d", (gint) start_type, (gint) stop_type); } else { GST_DEBUG_OBJECT (timidity, "received normal seek %d", (gint) start_type); update = FALSE; } gst_pad_push_event (timidity->srcpad, gst_timidity_get_new_segment_event (timidity, GST_FORMAT_TIME, update)); timidity->o_seek = TRUE; gst_pad_start_task (timidity->sinkpad, (GstTaskFunction) gst_timidity_loop, timidity->sinkpad); GST_PAD_STREAM_UNLOCK (timidity->sinkpad); GST_DEBUG ("seek done"); } res = TRUE; break; default: break; } g_object_unref (timidity); return res; }
/*********************************************************************************** * Seek implementation ***********************************************************************************/ static gboolean progress_buffer_perform_push_seek(ProgressBuffer *element, GstPad *pad, GstEvent *event) { GstFormat format; gdouble rate; GstSeekFlags flags; GstSeekType start_type, stop_type; gint64 position; GstSegment segment; gst_event_parse_seek(event, &rate, &format, &flags, &start_type, &position, &stop_type, NULL); if (format != GST_FORMAT_BYTES || start_type != GST_SEEK_TYPE_SET) return FALSE; if (stop_type != GST_SEEK_TYPE_NONE) { gst_element_message_full(GST_ELEMENT(element), GST_MESSAGE_WARNING, GST_CORE_ERROR, GST_CORE_ERROR_SEEK, g_strdup("stop_type != GST_SEEK_TYPE_NONE. Seeking to stop is not supported."), NULL, ("progressbuffer.c"), ("progress_buffer_perform_push_seek"), 0); return FALSE; } if (flags & GST_SEEK_FLAG_FLUSH) gst_pad_push_event(pad, gst_event_new_flush_start()); // Signal the task to stop if it's waiting. g_mutex_lock(&element->lock); element->srcresult = GST_FLOW_FLUSHING; g_cond_signal(&element->add_cond); g_mutex_unlock(&element->lock); GST_PAD_STREAM_LOCK(pad); // Wait for task to stop g_mutex_lock(&element->lock); element->srcresult = GST_FLOW_OK; #ifdef ENABLE_SOURCE_SEEKING element->instant_seek = (position >= element->sink_segment.start && (position - (gint64)element->sink_segment.position) <= element->bandwidth * element->wait_tolerance); if (element->instant_seek) { cache_set_read_position(element->cache, position - element->cache_read_offset); gst_segment_init(&segment, GST_FORMAT_BYTES); segment.rate = rate; segment.start = position; segment.stop = element->sink_segment.stop; segment.position = position; progress_buffer_set_pending_event(element, gst_event_new_segment(&segment)); } else { // Clear any pending events, since we doing seek. reset_eos(element, TRUE); } #else cache_set_read_position(element->cache, position - element->cache_read_offset); gst_segment_init(&segment, GST_FORMAT_BYTES); segment.rate = rate; segment.start = position; segment.stop = element->sink_segment.stop; segment.position = position; progress_buffer_set_pending_event(element, gst_event_new_segment(&segment)); #endif g_mutex_unlock(&element->lock); #ifdef ENABLE_SOURCE_SEEKING if (!element->instant_seek) { element->is_source_seeking = TRUE; if (!gst_pad_push_event(element->sinkpad, gst_event_new_seek(rate, GST_FORMAT_BYTES, flags, GST_SEEK_TYPE_SET, position, GST_SEEK_TYPE_NONE, 0))) { element->instant_seek = TRUE; cache_set_read_position(element->cache, position - element->cache_read_offset); gst_segment_init(&segment, GST_FORMAT_BYTES); segment.rate = rate; segment.start = position; segment.stop = element->sink_segment.stop; segment.position = position; progress_buffer_set_pending_event(element, gst_event_new_segment(&segment)); } element->is_source_seeking = FALSE; } #endif if (flags & GST_SEEK_FLAG_FLUSH) gst_pad_push_event(pad, gst_event_new_flush_stop(TRUE)); gst_pad_start_task(element->srcpad, progress_buffer_loop, element, NULL); GST_PAD_STREAM_UNLOCK(pad); // INLINE - gst_event_unref() gst_event_unref(event); 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_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_wavpack_parse_handle_seek_event (GstWavpackParse * wvparse, GstEvent * event) { GstSeekFlags seek_flags; GstSeekType start_type; GstSeekType stop_type; GstSegment segment; GstFormat format; gboolean only_update; gboolean flush, ret; gdouble speed; gint64 stop; gint64 start; /* sample we want to seek to */ gint64 byte_offset; /* byte offset the chunk we seek to starts at */ gint64 chunk_start; /* first sample in chunk we seek to */ guint rate; gint64 last_stop; if (wvparse->adapter) { GST_DEBUG_OBJECT (wvparse, "seeking in streaming mode not implemented yet"); return FALSE; } gst_event_parse_seek (event, &speed, &format, &seek_flags, &start_type, &start, &stop_type, &stop); if (format != GST_FORMAT_DEFAULT && format != GST_FORMAT_TIME) { GST_DEBUG ("seeking is only supported in TIME or DEFAULT format"); return FALSE; } if (speed < 0.0) { GST_DEBUG ("only forward playback supported, rate %f not allowed", speed); return FALSE; } GST_OBJECT_LOCK (wvparse); rate = wvparse->samplerate; if (rate == 0) { GST_OBJECT_UNLOCK (wvparse); GST_DEBUG ("haven't read header yet"); return FALSE; } /* figure out the last position we need to play. If it's configured (stop != * -1), use that, else we play until the total duration of the file */ if (stop == -1) stop = wvparse->segment.duration; /* convert from time to samples if necessary */ if (format == GST_FORMAT_TIME) { if (start_type != GST_SEEK_TYPE_NONE) start = gst_util_uint64_scale_int (start, rate, GST_SECOND); if (stop_type != GST_SEEK_TYPE_NONE) stop = gst_util_uint64_scale_int (stop, rate, GST_SECOND); } if (start < 0) { GST_OBJECT_UNLOCK (wvparse); GST_DEBUG_OBJECT (wvparse, "Invalid start sample %" G_GINT64_FORMAT, start); return FALSE; } flush = ((seek_flags & GST_SEEK_FLAG_FLUSH) != 0); /* operate on segment copy until we know the seek worked */ segment = wvparse->segment; gst_segment_set_seek (&segment, speed, GST_FORMAT_DEFAULT, seek_flags, start_type, start, stop_type, stop, &only_update); #if 0 if (only_update) { wvparse->segment = segment; gst_wavpack_parse_send_newsegment (wvparse, TRUE); goto done; } #endif gst_pad_push_event (wvparse->sinkpad, gst_event_new_flush_start ()); if (flush) { gst_pad_push_event (wvparse->srcpad, gst_event_new_flush_start ()); } else { gst_pad_pause_task (wvparse->sinkpad); } GST_PAD_STREAM_LOCK (wvparse->sinkpad); /* Save current position */ last_stop = wvparse->segment.last_stop; gst_pad_push_event (wvparse->sinkpad, gst_event_new_flush_stop ()); if (flush) { gst_pad_push_event (wvparse->srcpad, gst_event_new_flush_stop ()); } GST_DEBUG_OBJECT (wvparse, "Performing seek to %" GST_TIME_FORMAT " sample %" G_GINT64_FORMAT, GST_TIME_ARGS (segment.start * GST_SECOND / rate), start); ret = gst_wavpack_parse_scan_to_find_sample (wvparse, segment.start, &byte_offset, &chunk_start); if (ret) { GST_DEBUG_OBJECT (wvparse, "new offset: %" G_GINT64_FORMAT, byte_offset); wvparse->current_offset = byte_offset; /* we want to send a newsegment event with the actual seek position * as start, even though our first buffer might start before the * configured segment. We leave it up to the decoder or sink to crop * the output buffers accordingly */ wvparse->segment = segment; wvparse->segment.last_stop = chunk_start; wvparse->need_newsegment = TRUE; wvparse->discont = (last_stop != chunk_start) ? TRUE : FALSE; /* if we're doing a segment seek, post a SEGMENT_START message */ if (wvparse->segment.flags & GST_SEEK_FLAG_SEGMENT) { gst_element_post_message (GST_ELEMENT_CAST (wvparse), gst_message_new_segment_start (GST_OBJECT_CAST (wvparse), wvparse->segment.format, wvparse->segment.last_stop)); } } else { GST_DEBUG_OBJECT (wvparse, "seek failed: don't know where to seek to"); } GST_PAD_STREAM_UNLOCK (wvparse->sinkpad); GST_OBJECT_UNLOCK (wvparse); gst_pad_start_task (wvparse->sinkpad, (GstTaskFunction) gst_wavpack_parse_loop, wvparse); return ret; }
gboolean mpegts_base_handle_seek_event (MpegTSBase * base, GstPad * pad, GstEvent * event) { MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base); GstFlowReturn ret = GST_FLOW_ERROR; gdouble rate; gboolean flush; GstFormat format; GstSeekFlags flags; GstSeekType start_type, stop_type; gint64 start, stop; GstEvent *flush_event = NULL; gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start, &stop_type, &stop); if (format != GST_FORMAT_TIME) return FALSE; if (GST_EVENT_SEQNUM (event) == base->last_seek_seqnum) { GST_DEBUG_OBJECT (base, "Skipping already handled seek"); return TRUE; } if (base->mode == BASE_MODE_PUSHING) { /* First try if upstream supports seeking in TIME format */ if (gst_pad_push_event (base->sinkpad, gst_event_ref (event))) { GST_DEBUG ("upstream handled SEEK event"); return TRUE; } /* If the subclass can seek, do that */ if (klass->seek) { ret = klass->seek (base, event); if (G_UNLIKELY (ret != GST_FLOW_OK)) GST_WARNING ("seeking failed %s", gst_flow_get_name (ret)); else { GstEvent *new_seek; if (GST_CLOCK_TIME_IS_VALID (base->seek_offset)) { base->mode = BASE_MODE_SEEKING; new_seek = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, GST_SEEK_TYPE_SET, base->seek_offset, GST_SEEK_TYPE_NONE, -1); gst_event_set_seqnum (new_seek, GST_EVENT_SEQNUM (event)); if (!gst_pad_push_event (base->sinkpad, new_seek)) ret = GST_FLOW_ERROR; else base->last_seek_seqnum = GST_EVENT_SEQNUM (event); } base->mode = BASE_MODE_PUSHING; } } else { GST_WARNING ("subclass has no seek implementation"); } return ret == GST_FLOW_OK; } if (!klass->seek) { GST_WARNING ("subclass has no seek implementation"); return FALSE; } if (rate <= 0.0) { GST_WARNING ("Negative rate not supported"); return FALSE; } GST_DEBUG ("seek event, rate: %f start: %" GST_TIME_FORMAT " stop: %" GST_TIME_FORMAT, rate, GST_TIME_ARGS (start), GST_TIME_ARGS (stop)); flush = flags & GST_SEEK_FLAG_FLUSH; /* stop streaming, either by flushing or by pausing the task */ base->mode = BASE_MODE_SEEKING; if (flush) { GST_DEBUG_OBJECT (base, "sending flush start"); flush_event = gst_event_new_flush_start (); gst_event_set_seqnum (flush_event, GST_EVENT_SEQNUM (event)); gst_pad_push_event (base->sinkpad, gst_event_ref (flush_event)); GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, flush_event); } else gst_pad_pause_task (base->sinkpad); /* wait for streaming to finish */ GST_PAD_STREAM_LOCK (base->sinkpad); if (flush) { /* send a FLUSH_STOP for the sinkpad, since we need data for seeking */ GST_DEBUG_OBJECT (base, "sending flush stop"); flush_event = gst_event_new_flush_stop (TRUE); gst_event_set_seqnum (flush_event, GST_EVENT_SEQNUM (event)); /* ref for it to be reused later */ gst_pad_push_event (base->sinkpad, gst_event_ref (flush_event)); /* And actually flush our pending data but allow to preserve some info * to perform the seek */ mpegts_base_flush (base, FALSE); mpegts_packetizer_flush (base->packetizer, FALSE); } if (flags & (GST_SEEK_FLAG_SEGMENT)) { GST_WARNING ("seek flags 0x%x are not supported", (int) flags); goto done; } /* If the subclass can seek, do that */ ret = klass->seek (base, event); if (G_UNLIKELY (ret != GST_FLOW_OK)) GST_WARNING ("seeking failed %s", gst_flow_get_name (ret)); else base->last_seek_seqnum = GST_EVENT_SEQNUM (event); if (flush_event) { /* if we sent a FLUSH_START, we now send a FLUSH_STOP */ GST_DEBUG_OBJECT (base, "sending flush stop"); GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, flush_event); flush_event = NULL; } done: if (flush_event) gst_event_unref (flush_event); gst_pad_start_task (base->sinkpad, (GstTaskFunction) mpegts_base_loop, base, NULL); GST_PAD_STREAM_UNLOCK (base->sinkpad); return ret == GST_FLOW_OK; }
static gboolean gst_spc_dec_src_event (GstPad * pad, GstEvent * event) { GstSpcDec *spc = GST_SPC_DEC (gst_pad_get_parent (pad)); gboolean result = FALSE; switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEEK: { gdouble rate; GstFormat format; GstSeekFlags flags; GstSeekType start_type, stop_type; gint64 start, stop; gboolean flush; gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start, &stop_type, &stop); if (format != GST_FORMAT_TIME) { GST_DEBUG_OBJECT (spc, "seeking is only supported in TIME format"); break; } if (start_type != GST_SEEK_TYPE_SET || stop_type != GST_SEEK_TYPE_NONE) { GST_DEBUG_OBJECT (spc, "unsupported seek type"); break; } if (stop_type == GST_SEEK_TYPE_NONE) stop = GST_CLOCK_TIME_NONE; if (start_type == GST_SEEK_TYPE_SET) { guint64 cur = gst_util_uint64_scale (spc->byte_pos, GST_SECOND, 32000 * 2 * 2); guint64 dest = (guint64) start; dest = CLAMP (dest, 0, gst_spc_duration (spc) + gst_spc_fadeout (spc)); if (dest == cur) break; flush = (flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH; if (flush) { gst_pad_push_event (spc->srcpad, gst_event_new_flush_start ()); } else { gst_pad_stop_task (spc->srcpad); } GST_PAD_STREAM_LOCK (spc->srcpad); if (flags & GST_SEEK_FLAG_SEGMENT) { gst_element_post_message (GST_ELEMENT (spc), gst_message_new_segment_start (GST_OBJECT (spc), format, cur)); } if (flush) { gst_pad_push_event (spc->srcpad, gst_event_new_flush_stop ()); } if (stop == GST_CLOCK_TIME_NONE) stop = (guint64) (gst_spc_duration (spc) + gst_spc_fadeout (spc)); gst_pad_push_event (spc->srcpad, gst_event_new_new_segment (FALSE, rate, GST_FORMAT_TIME, dest, stop, dest)); /* spc->byte_pos += OSPC_Run(-1, NULL, (unsigned int) (gst_util_uint64_scale(dest - cur, 32000*2*2, GST_SECOND))); */ spc->seekpoint = gst_util_uint64_scale (dest, 32000 * 2 * 2, GST_SECOND); spc->seeking = TRUE; gst_pad_start_task (spc->srcpad, (GstTaskFunction) spc_play, spc->srcpad); GST_PAD_STREAM_UNLOCK (spc->srcpad); result = TRUE; } break; } default: break; } gst_event_unref (event); gst_object_unref (spc); return result; }
static gboolean gst_gme_dec_src_event (GstPad * pad, GstObject * parent, GstEvent * event) { GstGmeDec *gme = GST_GME_DEC (parent); gboolean result = FALSE; switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEEK: { gdouble rate; GstFormat format; GstSeekFlags flags; GstSeekType start_type, stop_type; gint64 start, stop; gboolean flush; gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start, &stop_type, &stop); gst_event_unref (event); if (format != GST_FORMAT_TIME) { GST_DEBUG_OBJECT (gme, "seeking is only supported in TIME format"); break; } if (start_type != GST_SEEK_TYPE_SET || stop_type != GST_SEEK_TYPE_NONE) { GST_DEBUG_OBJECT (gme, "unsupported seek type"); break; } if (stop_type == GST_SEEK_TYPE_NONE) stop = GST_CLOCK_TIME_NONE; if (start_type == GST_SEEK_TYPE_SET) { GstSegment seg; guint64 cur = gme_tell (gme->player) * GST_MSECOND; guint64 dest = (guint64) start; if (gme->total_duration != GST_CLOCK_TIME_NONE) dest = CLAMP (dest, 0, gme->total_duration); else dest = MAX (0, dest); if (dest == cur) break; flush = (flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH; if (flush) { gst_pad_push_event (gme->srcpad, gst_event_new_flush_start ()); } else { gst_pad_stop_task (gme->srcpad); } GST_PAD_STREAM_LOCK (gme->srcpad); if (flags & GST_SEEK_FLAG_SEGMENT) { gst_element_post_message (GST_ELEMENT (gme), gst_message_new_segment_start (GST_OBJECT (gme), format, cur)); } if (flush) { gst_pad_push_event (gme->srcpad, gst_event_new_flush_stop (TRUE)); } if (stop == GST_CLOCK_TIME_NONE && gme->total_duration != GST_CLOCK_TIME_NONE) stop = gme->total_duration; gst_segment_init (&seg, GST_FORMAT_TIME); seg.rate = rate; seg.start = dest; seg.stop = stop; seg.time = dest; gst_pad_push_event (gme->srcpad, gst_event_new_segment (&seg)); gme->seekpoint = dest / GST_MSECOND; /* nsecs to msecs */ gme->seeking = TRUE; gst_pad_start_task (gme->srcpad, (GstTaskFunction) gst_gme_play, gme->srcpad, NULL); GST_PAD_STREAM_UNLOCK (gme->srcpad); result = TRUE; } break; } default: result = gst_pad_push_event (gme->sinkpad, event); break; } return result; }
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; }
/* GstAggregator vmethods default implementations */ static gboolean _sink_event (GstAggregator * self, GstAggregatorPad * aggpad, GstEvent * event) { gboolean res = TRUE; GstPad *pad = GST_PAD (aggpad); GstAggregatorPrivate *priv = self->priv; switch (GST_EVENT_TYPE (event)) { case GST_EVENT_FLUSH_START: { _flush_start (self, aggpad, event); /* We forward only in one case: right after flush_seeking */ event = NULL; goto eat; } case GST_EVENT_FLUSH_STOP: { GST_DEBUG_OBJECT (aggpad, "Got FLUSH_STOP"); _aggpad_flush (aggpad, self); if (g_atomic_int_get (&priv->flush_seeking)) { g_atomic_int_set (&aggpad->priv->pending_flush_stop, FALSE); if (g_atomic_int_get (&priv->flush_seeking)) { if (_all_flush_stop_received (self)) { /* That means we received FLUSH_STOP/FLUSH_STOP on * all sinkpads -- Seeking is Done... sending FLUSH_STOP */ _flush (self); gst_pad_push_event (self->srcpad, event); priv->send_eos = TRUE; event = NULL; QUEUE_PUSH (self); GST_INFO_OBJECT (self, "Releasing source pad STREAM_LOCK"); GST_PAD_STREAM_UNLOCK (self->srcpad); _start_srcpad_task (self); } } } /* We never forward the event */ goto eat; } case GST_EVENT_EOS: { GST_DEBUG_OBJECT (aggpad, "EOS"); /* We still have a buffer, and we don't want the subclass to have to * check for it. Mark pending_eos, eos will be set when steal_buffer is * called */ PAD_LOCK_EVENT (aggpad); if (!aggpad->buffer) { aggpad->eos = TRUE; } else { aggpad->priv->pending_eos = TRUE; } PAD_UNLOCK_EVENT (aggpad); QUEUE_PUSH (self); goto eat; } case GST_EVENT_SEGMENT: { PAD_LOCK_EVENT (aggpad); gst_event_copy_segment (event, &aggpad->segment); self->priv->seqnum = gst_event_get_seqnum (event); PAD_UNLOCK_EVENT (aggpad); goto eat; } case GST_EVENT_STREAM_START: { goto eat; } case GST_EVENT_TAG: { GstTagList *tags; gst_event_parse_tag (event, &tags); if (gst_tag_list_get_scope (tags) == GST_TAG_SCOPE_STREAM) { gst_aggregator_merge_tags (self, tags, GST_TAG_MERGE_REPLACE); gst_event_unref (event); event = NULL; goto eat; } break; } default: { break; } } GST_DEBUG_OBJECT (pad, "Forwarding event: %" GST_PTR_FORMAT, event); return gst_pad_event_default (pad, GST_OBJECT (self), event); eat: GST_DEBUG_OBJECT (pad, "Eating event: %" GST_PTR_FORMAT, event); if (event) gst_event_unref (event); return res; }
/* 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_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; } }