static gboolean vorbis_dec_sink_event (GstPad * pad, GstEvent * event) { gboolean ret = FALSE; GstVorbisDec *dec; dec = GST_VORBIS_DEC (gst_pad_get_parent (pad)); GST_LOG_OBJECT (dec, "handling event"); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_EOS: if (dec->segment.rate < 0.0) vorbis_dec_chain_reverse (dec, TRUE, NULL); ret = gst_pad_push_event (dec->srcpad, event); break; case GST_EVENT_FLUSH_START: ret = gst_pad_push_event (dec->srcpad, event); break; case GST_EVENT_FLUSH_STOP: /* here we must clean any state in the decoder */ #ifdef HAVE_VORBIS_SYNTHESIS_RESTART vorbis_synthesis_restart (&dec->vd); #endif gst_vorbis_dec_reset (dec); ret = gst_pad_push_event (dec->srcpad, event); break; case GST_EVENT_NEWSEGMENT: { GstFormat format; gdouble rate, arate; gint64 start, stop, time; gboolean update; gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, &start, &stop, &time); /* we need time for now */ if (format != GST_FORMAT_TIME) goto newseg_wrong_format; GST_DEBUG_OBJECT (dec, "newsegment: update %d, rate %g, arate %g, start %" GST_TIME_FORMAT ", stop %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT, update, rate, arate, GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (time)); /* now configure the values */ gst_segment_set_newsegment_full (&dec->segment, update, rate, arate, format, start, stop, time); dec->seqnum = gst_event_get_seqnum (event); if (dec->initialized) /* and forward */ ret = gst_pad_push_event (dec->srcpad, event); else { /* store it to send once we're initialized */ dec->pendingevents = g_list_append (dec->pendingevents, event); ret = TRUE; } break; } case GST_EVENT_TAG: { if (dec->initialized) /* and forward */ ret = gst_pad_push_event (dec->srcpad, event); else { /* store it to send once we're initialized */ dec->pendingevents = g_list_append (dec->pendingevents, event); ret = TRUE; } break; } default: ret = gst_pad_push_event (dec->srcpad, event); break; } done: gst_object_unref (dec); return ret; /* ERRORS */ newseg_wrong_format: { GST_DEBUG_OBJECT (dec, "received non TIME newsegment"); goto done; } }
static gboolean dxr3videosink_handle_event (GstPad * pad, GstEvent * event) { GstEventType type; Dxr3VideoSink *sink; sink = DXR3VIDEOSINK (gst_pad_get_parent (pad)); type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN; switch (type) { case GST_EVENT_EMPTY: //fprintf (stderr, "++++++ Video empty event\n"); { /* FIXME: Handle this with a discontinuity or something. */ /* Write an MPEG2 sequence end code, to ensure that the card actually displays the last picture. Apparently some DVDs are encoded without proper sequence end codes. */ static const guint8 sec[4] = { 0x00, 0x00, 0x01, 0xb7 }; if (sink->cur_buf != NULL) { dxr3videosink_write_data (sink, 0); } write (sink->video_fd, &sec, 4); } break; case GST_EVENT_DISCONTINUOUS: //fprintf (stderr, "++++++ Video discont event\n"); { gint64 time; gboolean has_time; unsigned cur_scr, mpeg_scr, diff; has_time = gst_event_discont_get_value (event, GST_FORMAT_TIME, &time); if (has_time) { /* fprintf (stderr, "^^^^^^ Discontinuous event has time %.4f\n", */ /* (double) time / GST_SECOND); */ /* If the SCR in the card is way off, fix it. */ ioctl (sink->control_fd, EM8300_IOCTL_SCR_GET, &cur_scr); mpeg_scr = MPEGTIME_TO_DXRTIME (GSTTIME_TO_MPEGTIME (time)); diff = cur_scr > mpeg_scr ? cur_scr - mpeg_scr : mpeg_scr - cur_scr; if (diff > 1800) { unsigned zero = 0; /* fprintf (stderr, "====== Adjusting SCR from video\n"); */ ioctl (sink->control_fd, EM8300_IOCTL_SCR_SET, &zero); ioctl (sink->control_fd, EM8300_IOCTL_SCR_SET, &mpeg_scr); } } else { /* fprintf (stderr, "^^^^^^ Discontinuous event has no time\n"); */ } } break; case GST_EVENT_FLUSH: dxr3videosink_reset_parser (sink); break; default: gst_pad_event_default (pad, event); break; } return TRUE; }
static GstPadProbeReturn handle_mq_input (GstPad * pad, GstPadProbeInfo * info, MqStreamCtx * ctx) { GstSplitMuxSink *splitmux = ctx->splitmux; GstBuffer *buf; MqStreamBuf *buf_info = NULL; GstClockTime ts; gboolean loop_again; gboolean keyframe = FALSE; GST_LOG_OBJECT (pad, "Fired probe type 0x%x\n", info->type); /* FIXME: Handle buffer lists, until then make it clear they won't work */ if (info->type & GST_PAD_PROBE_TYPE_BUFFER_LIST) { g_warning ("Buffer list handling not implemented"); return GST_PAD_PROBE_DROP; } if (info->type & GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM) { GstEvent *event = gst_pad_probe_info_get_event (info); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEGMENT: gst_event_copy_segment (event, &ctx->in_segment); break; case GST_EVENT_FLUSH_STOP: GST_SPLITMUX_LOCK (splitmux); gst_segment_init (&ctx->in_segment, GST_FORMAT_UNDEFINED); ctx->in_eos = FALSE; ctx->in_bytes = 0; ctx->in_running_time = 0; GST_SPLITMUX_UNLOCK (splitmux); break; case GST_EVENT_EOS: GST_SPLITMUX_LOCK (splitmux); ctx->in_eos = TRUE; if (splitmux->state == SPLITMUX_STATE_STOPPED) goto beach; if (ctx->is_video) { GST_INFO_OBJECT (splitmux, "Got Video EOS. Finishing up"); /* Act as if this is a new keyframe with infinite timestamp */ splitmux->max_in_running_time = GST_CLOCK_TIME_NONE; splitmux->state = SPLITMUX_STATE_WAITING_GOP_COMPLETE; /* Wake up other input pads to collect this GOP */ GST_SPLITMUX_BROADCAST (splitmux); check_completed_gop (splitmux, ctx); } else if (splitmux->state == SPLITMUX_STATE_WAITING_GOP_COMPLETE) { /* If we are waiting for a GOP to be completed (ie, for aux * pads to catch up), then this pad is complete, so check * if the whole GOP is. */ check_completed_gop (splitmux, ctx); } GST_SPLITMUX_UNLOCK (splitmux); break; default: break; } return GST_PAD_PROBE_PASS; } buf = gst_pad_probe_info_get_buffer (info); ctx->in_running_time = gst_segment_to_running_time (&ctx->in_segment, GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (buf)); buf_info = mq_stream_buf_new (); if (GST_BUFFER_PTS_IS_VALID (buf)) ts = GST_BUFFER_PTS (buf); else ts = GST_BUFFER_DTS (buf); GST_SPLITMUX_LOCK (splitmux); if (splitmux->state == SPLITMUX_STATE_STOPPED) goto beach; /* If this buffer has a timestamp, advance the input timestamp of the * stream */ if (GST_CLOCK_TIME_IS_VALID (ts)) { GstClockTime running_time = gst_segment_to_running_time (&ctx->in_segment, GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (buf)); if (GST_CLOCK_TIME_IS_VALID (running_time) && (ctx->in_running_time == GST_CLOCK_TIME_NONE || running_time > ctx->in_running_time)) ctx->in_running_time = running_time; } /* Try to make sure we have a valid running time */ if (!GST_CLOCK_TIME_IS_VALID (ctx->in_running_time)) { ctx->in_running_time = gst_segment_to_running_time (&ctx->in_segment, GST_FORMAT_TIME, ctx->in_segment.start); } buf_info->run_ts = ctx->in_running_time; buf_info->buf_size = gst_buffer_get_size (buf); /* Update total input byte counter for overflow detect */ ctx->in_bytes += buf_info->buf_size; GST_DEBUG_OBJECT (pad, "Buf TS %" GST_TIME_FORMAT " total in_bytes %" G_GSIZE_FORMAT, GST_TIME_ARGS (buf_info->run_ts), ctx->in_bytes); loop_again = TRUE; do { if (ctx->flushing) break; switch (splitmux->state) { case SPLITMUX_STATE_COLLECTING_GOP_START: if (ctx->is_video) { /* If a keyframe, we have a complete GOP */ if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) || !GST_CLOCK_TIME_IS_VALID (ctx->in_running_time) || splitmux->max_in_running_time >= ctx->in_running_time) { /* Pass this buffer through */ loop_again = FALSE; break; } GST_INFO_OBJECT (pad, "Have keyframe with running time %" GST_TIME_FORMAT, GST_TIME_ARGS (ctx->in_running_time)); keyframe = TRUE; splitmux->state = SPLITMUX_STATE_WAITING_GOP_COMPLETE; splitmux->max_in_running_time = ctx->in_running_time; /* Wake up other input pads to collect this GOP */ GST_SPLITMUX_BROADCAST (splitmux); check_completed_gop (splitmux, ctx); } else { /* We're still waiting for a keyframe on the video pad, sleep */ GST_LOG_OBJECT (pad, "Sleeping for GOP start"); GST_SPLITMUX_WAIT (splitmux); GST_LOG_OBJECT (pad, "Done sleeping for GOP start state now %d", splitmux->state); } break; case SPLITMUX_STATE_WAITING_GOP_COMPLETE: /* After a GOP start is found, this buffer might complete the GOP */ /* If we overran the target timestamp, it might be time to process * the GOP, otherwise bail out for more data */ GST_LOG_OBJECT (pad, "Checking TS %" GST_TIME_FORMAT " against max %" GST_TIME_FORMAT, GST_TIME_ARGS (ctx->in_running_time), GST_TIME_ARGS (splitmux->max_in_running_time)); if (ctx->in_running_time < splitmux->max_in_running_time) { loop_again = FALSE; break; } GST_LOG_OBJECT (pad, "Collected last packet of GOP. Checking other pads"); check_completed_gop (splitmux, ctx); break; case SPLITMUX_STATE_ENDING_FILE: case SPLITMUX_STATE_START_NEXT_FRAGMENT: /* A fragment is ending, wait until that's done before continuing */ GST_DEBUG_OBJECT (pad, "Sleeping for fragment restart"); GST_SPLITMUX_WAIT (splitmux); GST_DEBUG_OBJECT (pad, "Done sleeping for fragment restart state now %d", splitmux->state); break; default: loop_again = FALSE; break; } } while (loop_again); if (keyframe) { splitmux->queued_gops++; buf_info->keyframe = TRUE; } /* Now add this buffer to the queue just before returning */ g_queue_push_head (&ctx->queued_bufs, buf_info); /* Check the buffer will fit in the mq */ check_queue_length (splitmux, ctx); GST_LOG_OBJECT (pad, "Returning to queue buffer %" GST_PTR_FORMAT " run ts %" GST_TIME_FORMAT, buf, GST_TIME_ARGS (ctx->in_running_time)); GST_SPLITMUX_UNLOCK (splitmux); return GST_PAD_PROBE_PASS; beach: GST_SPLITMUX_UNLOCK (splitmux); if (buf_info) mq_stream_buf_free (buf_info); return GST_PAD_PROBE_PASS; }
static gboolean pad_event (GstPad *pad, GstEvent *event) { GstOmxBaseFilter *self; GOmxCore *gomx; GOmxPort *in_port; gboolean ret = TRUE; self = GST_OMX_BASE_FILTER (GST_OBJECT_PARENT (pad)); gomx = self->gomx; in_port = self->in_port; GST_LOG_OBJECT (self, "begin"); GST_INFO_OBJECT (self, "event: %s", GST_EVENT_TYPE_NAME (event)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_EOS: /* if we are init'ed, and there is a running loop; then * if we get a buffer to inform it of EOS, let it handle the rest * in any other case, we send EOS */ if (self->ready && self->last_pad_push_return == GST_FLOW_OK) { /* send buffer with eos flag */ /** @todo move to util */ { OMX_BUFFERHEADERTYPE *omx_buffer; GST_LOG_OBJECT (self, "request buffer"); omx_buffer = g_omx_port_request_buffer (in_port); if (G_LIKELY (omx_buffer)) { omx_buffer->nFlags |= OMX_BUFFERFLAG_EOS; GST_LOG_OBJECT (self, "release_buffer"); /* foo_buffer_untaint (omx_buffer); */ g_omx_port_release_buffer (in_port, omx_buffer); /* loop handles EOS, eat it here */ gst_event_unref (event); break; } } } /* we tried, but it's up to us here */ ret = gst_pad_push_event (self->srcpad, event); break; case GST_EVENT_FLUSH_START: gst_pad_push_event (self->srcpad, event); self->last_pad_push_return = GST_FLOW_WRONG_STATE; g_omx_core_flush_start (gomx); gst_pad_pause_task (self->srcpad); ret = TRUE; break; case GST_EVENT_FLUSH_STOP: gst_pad_push_event (self->srcpad, event); self->last_pad_push_return = GST_FLOW_OK; g_omx_core_flush_stop (gomx); if (self->ready) gst_pad_start_task (self->srcpad, output_loop, self->srcpad); ret = TRUE; break; case GST_EVENT_NEWSEGMENT: ret = gst_pad_push_event (self->srcpad, event); break; default: ret = gst_pad_push_event (self->srcpad, event); break; } GST_LOG_OBJECT (self, "end"); return ret; }
static gboolean celt_dec_sink_event (GstPad * pad, GstEvent * event) { GstCeltDec *dec; gboolean ret = FALSE; dec = GST_CELT_DEC (gst_pad_get_parent (pad)); GST_LOG_OBJECT (dec, "handling %s event", GST_EVENT_TYPE_NAME (event)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_NEWSEGMENT:{ GstFormat format; gdouble rate, arate; gint64 start, stop, time; gboolean update; gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, &start, &stop, &time); if (format != GST_FORMAT_TIME) goto newseg_wrong_format; if (rate <= 0.0) goto newseg_wrong_rate; if (update) { /* time progressed without data, see if we can fill the gap with * some concealment data */ if (dec->segment.last_stop < start) { GstClockTime duration; duration = start - dec->segment.last_stop; celt_dec_chain_parse_data (dec, NULL, dec->segment.last_stop, duration); } } /* now configure the values */ gst_segment_set_newsegment_full (&dec->segment, update, rate, arate, GST_FORMAT_TIME, start, stop, time); dec->granulepos = -1; GST_DEBUG_OBJECT (dec, "segment now: cur = %" GST_TIME_FORMAT " [%" GST_TIME_FORMAT " - %" GST_TIME_FORMAT "]", GST_TIME_ARGS (dec->segment.last_stop), GST_TIME_ARGS (dec->segment.start), GST_TIME_ARGS (dec->segment.stop)); ret = gst_pad_push_event (dec->srcpad, event); break; } default: ret = gst_pad_event_default (pad, event); break; } gst_object_unref (dec); return ret; /* ERRORS */ newseg_wrong_format: { GST_DEBUG_OBJECT (dec, "received non TIME newsegment"); gst_object_unref (dec); return FALSE; } newseg_wrong_rate: { GST_DEBUG_OBJECT (dec, "negative rates not supported yet"); gst_object_unref (dec); return FALSE; } }
static gboolean rsn_audiomunge_sink_event (GstPad * pad, GstEvent * event) { gboolean ret = FALSE; RsnAudioMunge *munge = RSN_AUDIOMUNGE (gst_pad_get_parent (pad)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_FLUSH_STOP: rsn_audiomunge_reset (munge); ret = gst_pad_push_event (munge->srcpad, event); break; case GST_EVENT_NEWSEGMENT: { GstSegment *segment; gboolean update; GstFormat format; gdouble rate, arate; gint64 start, stop, time; gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, &start, &stop, &time); /* we need TIME format */ if (format != GST_FORMAT_TIME) goto newseg_wrong_format; /* now configure the values */ segment = &munge->sink_segment; gst_segment_set_newsegment_full (segment, update, rate, arate, format, start, stop, time); if (munge->have_audio) { ret = gst_pad_push_event (munge->srcpad, event); break; } /* * FIXME: * If the accum >= threshold or we're in a still frame and there's been * no audio received, then we need to generate some audio data. * If caused by a segment start update (time advancing in a gap) adjust * the new-segment and send the buffer. * * Otherwise, send the buffer before the newsegment, so that it appears * in the closing segment. */ if (!update) { GST_DEBUG_OBJECT (munge, "Sending newsegment: start %" GST_TIME_FORMAT " stop %" GST_TIME_FORMAT " accum now %" GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (segment->accum)); ret = gst_pad_push_event (munge->srcpad, event); } if (segment->accum >= AUDIO_FILL_THRESHOLD || munge->in_still) { g_print ("*********** Sending audio fill: accum = %" GST_TIME_FORMAT " still-state=%d\n", GST_TIME_ARGS (segment->accum), munge->in_still); /* Just generate a 100ms silence buffer for now. FIXME: Fill the gap */ if (rsn_audiomunge_make_audio (munge, segment->start, GST_SECOND / 10) == GST_FLOW_OK) munge->have_audio = TRUE; } else { GST_LOG_OBJECT (munge, "Not sending audio fill buffer: " "segment accum below thresh: accum = %" GST_TIME_FORMAT, GST_TIME_ARGS (segment->accum)); } if (update) { GST_DEBUG_OBJECT (munge, "Sending newsegment: start %" GST_TIME_FORMAT " stop %" GST_TIME_FORMAT " accum now %" GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (segment->accum)); ret = gst_pad_push_event (munge->srcpad, event); } break; } case GST_EVENT_CUSTOM_DOWNSTREAM: { const GstStructure *s = gst_event_get_structure (event); if (s && gst_structure_has_name (s, "application/x-gst-dvd")) rsn_audiomunge_handle_dvd_event (munge, event); ret = gst_pad_push_event (munge->srcpad, event); break; } default: ret = gst_pad_push_event (munge->srcpad, event); break; } return ret; newseg_wrong_format: GST_DEBUG_OBJECT (munge, "received non TIME newsegment"); gst_event_unref (event); gst_object_unref (munge); return FALSE; }
static gboolean gst_base_video_codec_src_event (GstPad * pad, GstEvent * event) { GstBaseVideoCodec *base_video_codec; gboolean res = FALSE; base_video_codec = GST_BASE_VIDEO_CODEC (gst_pad_get_parent (pad)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEEK: { GstFormat format, tformat; gdouble rate; GstEvent *real_seek; GstSeekFlags flags; GstSeekType cur_type, stop_type; gint64 cur, stop; gint64 tcur, tstop; gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, &stop_type, &stop); gst_event_unref (event); tformat = GST_FORMAT_TIME; res = gst_base_video_encoded_video_convert (&base_video_codec->state, format, cur, &tformat, &tcur); if (!res) goto convert_error; res = gst_base_video_encoded_video_convert (&base_video_codec->state, format, stop, &tformat, &tstop); if (!res) goto convert_error; real_seek = gst_event_new_seek (rate, GST_FORMAT_TIME, flags, cur_type, tcur, stop_type, tstop); res = gst_pad_push_event (base_video_codec->sinkpad, real_seek); break; } #if 0 case GST_EVENT_QOS: { gdouble proportion; GstClockTimeDiff diff; GstClockTime timestamp; gst_event_parse_qos (event, &proportion, &diff, ×tamp); GST_OBJECT_LOCK (base_video_codec); base_video_codec->proportion = proportion; base_video_codec->earliest_time = timestamp + diff; GST_OBJECT_UNLOCK (base_video_codec); GST_DEBUG_OBJECT (base_video_codec, "got QoS %" GST_TIME_FORMAT ", %" G_GINT64_FORMAT, GST_TIME_ARGS (timestamp), diff); res = gst_pad_push_event (base_video_codec->sinkpad, event); break; } #endif default: res = gst_pad_push_event (base_video_codec->sinkpad, event); break; } done: gst_object_unref (base_video_codec); return res; convert_error: GST_DEBUG_OBJECT (base_video_codec, "could not convert format"); goto done; }
static gboolean gst_interlace_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) { gboolean ret; GstInterlace *interlace; interlace = GST_INTERLACE (parent); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_FLUSH_START: GST_DEBUG_OBJECT (interlace, "handling FLUSH_START"); ret = gst_pad_push_event (interlace->srcpad, event); break; case GST_EVENT_FLUSH_STOP: GST_DEBUG_OBJECT (interlace, "handling FLUSH_STOP"); gst_interlace_reset (interlace); ret = gst_pad_push_event (interlace->srcpad, event); break; case GST_EVENT_EOS: #if 0 /* FIXME revive this when we output ONEFIELD and RFF buffers */ { gint num_fields; const PulldownFormat *format = &formats[interlace->pattern]; num_fields = format->n_fields[interlace->phase_index] - interlace->stored_fields_pushed; interlace->stored_fields_pushed = 0; /* on EOS we want to push as many sane frames as are left */ while (num_fields > 1) { GstBuffer *output_buffer; /* make metadata writable before editing it */ interlace->stored_frame = gst_buffer_make_metadata_writable (interlace->stored_frame); num_fields -= 2; gst_interlace_decorate_buffer (interlace, interlace->stored_frame, n_fields, FALSE); /* ref output_buffer/stored frame because we want to keep it for now * and pushing gives away a ref */ output_buffer = gst_buffer_ref (interlace->stored_frame); if (gst_pad_push (interlace->srcpad, output_buffer)) { GST_DEBUG_OBJECT (interlace, "Failed to push buffer %p", output_buffer); return FALSE; } output_buffer = NULL; if (num_fields <= 1) { gst_buffer_unref (interlace->stored_frame); interlace->stored_frame = NULL; break; } } /* increment the phase index */ interlace->phase_index++; if (!format->n_fields[interlace->phase_index]) { interlace->phase_index = 0; } } #endif ret = gst_pad_push_event (interlace->srcpad, event); break; case GST_EVENT_CAPS: { GstCaps *caps; gst_event_parse_caps (event, &caps); ret = gst_interlace_setcaps (interlace, caps); gst_event_unref (event); break; } default: ret = gst_pad_push_event (interlace->srcpad, 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; }
static gboolean gst_merger_event_srcv (GstPad * pad, GstObject * parent, GstEvent * event) { GstMerger *merger = GST_MERGER (parent); gboolean ret; GST_DEBUG_OBJECT (pad, "got source event %s", GST_EVENT_TYPE_NAME (event)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_CAPS: { GstCaps *caps; gst_event_parse_caps (event, &caps); GST_DEBUG_OBJECT (pad, "event caps are %" GST_PTR_FORMAT, caps); break; } case GST_EVENT_EOS: process_eos (merger); gst_event_unref (event); ret = TRUE; break; case GST_EVENT_SEGMENT: gst_event_copy_segment (event, &merger->s_segment); GST_DEBUG_OBJECT (merger, "segment: %" GST_SEGMENT_FORMAT, &merger->s_segment); break; case GST_EVENT_QOS: { GstQOSType type; gdouble proportion; GstClockTimeDiff diff; GstClockTime timestamp; gst_event_parse_qos (event, &type, &proportion, &diff, ×tamp); if (type == GST_QOS_TYPE_UNDERFLOW) { GST_INFO_OBJECT (pad, "got QOS event UNDERFLOW proportion %f diff %" PRIu64 " ts %" PRIu64, proportion, diff, timestamp); GST_OBJECT_LOCK (merger); gint num, den; { GstCaps *caps = gst_pad_get_current_caps (merger->srcv_pad); GstStructure *s = gst_caps_get_structure (caps, 0); gst_structure_get_fraction (s, "framerate", &num, &den); gst_caps_unref (caps); } int nb_bufs = 0.5 + diff / (1e9 * den / num); GST_WARNING_OBJECT (merger, "Discarding %d buffers", nb_bufs); g_queue_pop_head (&merger->bufs_l); g_queue_pop_head (&merger->bufs_r); GST_OBJECT_UNLOCK (merger); } else { GST_WARNING_OBJECT (pad, "QOS type %d not implemented", type); ret = gst_pad_event_default (pad, parent, event); } } break; default: GST_INFO_OBJECT (pad, "got source event %s", GST_EVENT_TYPE_NAME (event)); ret = gst_pad_event_default (pad, parent, event); break; } return ret; }
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_two_lame_sink_event (GstPad * pad, GstEvent * event) { gboolean ret; GstTwoLame *twolame; twolame = GST_TWO_LAME (gst_pad_get_parent (pad)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_EOS:{ GST_DEBUG_OBJECT (twolame, "handling EOS event"); if (twolame->glopts != NULL) { GstBuffer *buf; gint size; buf = gst_buffer_new_and_alloc (16384); size = twolame_encode_flush (twolame->glopts, GST_BUFFER_DATA (buf), 16394); if (size > 0 && twolame->last_flow == GST_FLOW_OK) { gint64 duration; duration = gst_util_uint64_scale (size, 8 * GST_SECOND, 1000 * twolame->bitrate); if (twolame->last_ts == GST_CLOCK_TIME_NONE) { twolame->last_ts = twolame->eos_ts; twolame->last_duration = duration; } else { twolame->last_duration += duration; } GST_BUFFER_TIMESTAMP (buf) = twolame->last_ts; GST_BUFFER_DURATION (buf) = twolame->last_duration; twolame->last_ts = GST_CLOCK_TIME_NONE; GST_BUFFER_SIZE (buf) = size; GST_DEBUG_OBJECT (twolame, "pushing final packet of %u bytes", size); gst_buffer_set_caps (buf, GST_PAD_CAPS (twolame->srcpad)); gst_pad_push (twolame->srcpad, buf); } else { GST_DEBUG_OBJECT (twolame, "no final packet (size=%d, last_flow=%s)", size, gst_flow_get_name (twolame->last_flow)); gst_buffer_unref (buf); } } ret = gst_pad_event_default (pad, event); break; } case GST_EVENT_FLUSH_START: GST_DEBUG_OBJECT (twolame, "handling FLUSH start event"); /* forward event */ ret = gst_pad_push_event (twolame->srcpad, event); break; case GST_EVENT_FLUSH_STOP: { guchar *mp3_data = NULL; gint mp3_buffer_size, mp3_size = 0; GST_DEBUG_OBJECT (twolame, "handling FLUSH stop event"); /* clear buffers */ mp3_buffer_size = 16384; mp3_data = g_malloc (mp3_buffer_size); mp3_size = twolame_encode_flush (twolame->glopts, mp3_data, mp3_buffer_size); ret = gst_pad_push_event (twolame->srcpad, event); g_free (mp3_data); break; } default: ret = gst_pad_event_default (pad, event); break; } gst_object_unref (twolame); 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) { 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); } else { /* need to track segment for proper running time */ gst_event_copy_segment (event, &trans->segment); } } if (GST_EVENT_TYPE (event) == GST_EVENT_GAP && trans->have_segment && trans->segment.format == GST_FORMAT_TIME) { GstClockTime start, dur; gst_event_parse_gap (event, &start, &dur); if (GST_CLOCK_TIME_IS_VALID (start)) { start = gst_segment_to_running_time (&trans->segment, GST_FORMAT_TIME, start); gst_identity_do_sync (identity, start); /* also transform GAP timestamp similar to buffer timestamps */ if (identity->single_segment) { gst_event_unref (event); event = gst_event_new_gap (start, dur); } } } /* Reset previous timestamp, duration and offsets on SEGMENT * 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 gst_vdp_mpeg_dec_sink_event (GstPad * pad, GstEvent * event) { GstVdpMpegDec *mpeg_dec = GST_VDP_MPEG_DEC (gst_pad_get_parent (pad)); gboolean res; switch (GST_EVENT_TYPE (event)) { case GST_EVENT_FLUSH_STOP: { GST_DEBUG_OBJECT (mpeg_dec, "flush stop"); gst_vdp_mpeg_dec_flush (mpeg_dec); res = gst_pad_push_event (mpeg_dec->src, event); break; } case GST_EVENT_NEWSEGMENT: { gboolean update; gdouble rate; GstFormat format; gint64 start; gint64 stop; gint64 position; gst_event_parse_new_segment (event, &update, &rate, &format, &start, &stop, &position); if (format != GST_FORMAT_TIME) { if (!gst_vdp_mpeg_dec_convert (mpeg_dec, format, start, GST_FORMAT_TIME, &start)) goto convert_error; if (!gst_vdp_mpeg_dec_convert (mpeg_dec, format, stop, GST_FORMAT_TIME, &stop)) goto convert_error; if (!gst_vdp_mpeg_dec_convert (mpeg_dec, format, position, GST_FORMAT_TIME, &position)) goto convert_error; gst_event_unref (event); event = gst_event_new_new_segment (update, rate, GST_FORMAT_TIME, start, stop, position); } g_mutex_lock (mpeg_dec->mutex); /* if we seek ourselves we don't push out a newsegment now since we * use the calculated timestamp of the first frame for this */ if (mpeg_dec->seeking) { gst_event_unref (event); res = TRUE; g_mutex_unlock (mpeg_dec->mutex); goto done; } g_mutex_unlock (mpeg_dec->mutex); GST_DEBUG_OBJECT (mpeg_dec, "Pushing new segment update %d format %d start %" GST_TIME_FORMAT " stop %" GST_TIME_FORMAT " position %" GST_TIME_FORMAT, update, format, GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (position)); convert_error: res = gst_pad_push_event (mpeg_dec->src, event); break; } default: res = gst_pad_event_default (pad, event); } done: gst_object_unref (mpeg_dec); return res; }
static GstFlowReturn xing_mp3_encoder_chain(GstPad *pad, GstBuffer *buf) { XingMp3Encoder *encoder; GstFlowReturn ret = GST_FLOW_OK; g_return_val_if_fail(pad != NULL, GST_FLOW_ERROR); g_return_val_if_fail(GST_IS_PAD(pad), GST_FLOW_ERROR); g_return_val_if_fail(buf != NULL, GST_FLOW_ERROR); encoder = XING_MP3_ENCODER(gst_pad_get_parent(pad)); if(GST_IS_EVENT(buf)) { GstEvent *event = GST_EVENT(buf); switch(GST_EVENT_TYPE(event)) { case GST_EVENT_EOS: encoder->at_end_of_stream = TRUE; gst_event_unref(event); break; default: gst_pad_event_default(pad, event); return ret; } } else { guchar *data; gulong size; gulong i, j; float **buffer; gint buf_size; gint buf_pos; if(!encoder->is_initialized) { gst_buffer_unref(buf); GST_ELEMENT_ERROR(encoder, CORE, NEGOTIATION, (NULL), ("Encoder not initialized")); return GST_FLOW_UNEXPECTED; } if(!encoder->header_sent) { E_CONTROL control; MPEG_HEAD head; guchar output_buffer[OUTPUT_BUFFER_SIZE]; gint buf_len; if(!encoder->use_cbr) { GstFlowReturn push_ret; hx_mp3enc_l3_info_ec(encoder->xing_encoder, &control); hx_mp3enc_l3_info_head(encoder->xing_encoder, &head); buf_len = XingHeader(control.samprate, head.mode, control.cr_bit, control.original, VBR_SCALE_FLAG /* FRAMES_FLAG | BYTES_FLAG | TOC_FLAG */, 0, 0, control.vbr_flag ? control.vbr_mnr : -1, NULL, output_buffer, 0, 0, 0); if((push_ret = xing_mp3_encoder_push_buffer(encoder, output_buffer, buf_len)) != GST_FLOW_OK) { gst_buffer_unref(buf); return push_ret; } } encoder->header_sent = TRUE; } data = (guchar *)GST_BUFFER_DATA(buf); buf_size = GST_BUFFER_SIZE(buf); buf_pos = 0; /* Transfer incoming data to internal buffer. * TODO: Use a ring buffer, avoid memmove () */ while(buf_pos < buf_size) { gint gulp = MIN(buf_size - buf_pos, INPUT_BUFFER_SIZE - encoder->input_buffer_pos); memcpy(encoder->input_buffer + encoder->input_buffer_pos, data + buf_pos, gulp); encoder->samples_in += gulp / (2 * encoder->channels); encoder->input_buffer_pos += gulp; buf_pos += gulp; /* Pass data on to encoder */ while(encoder->input_buffer_pos >= encoder->bytes_per_frame) { guchar output_buffer[OUTPUT_BUFFER_SIZE]; IN_OUT x; x = hx_mp3enc_mp3_encode_frame(encoder->xing_encoder, encoder->input_buffer, output_buffer); if(x.in_bytes == 0 && x.out_bytes == 0) { break; } memmove(encoder->input_buffer, encoder->input_buffer + x.in_bytes, encoder->input_buffer_pos - x.in_bytes); encoder->input_buffer_pos -= x.in_bytes; /* Accept output from encoder and pass it on. * TODO: Do this less often and save CPU */ if(x.out_bytes > 0) { GstFlowReturn push_ret; if((push_ret = xing_mp3_encoder_push_buffer(encoder, output_buffer, x.out_bytes)) != GST_FLOW_OK) { gst_buffer_unref(buf); return push_ret; } } } } gst_buffer_unref(buf); } if(encoder->at_end_of_stream) { gst_pad_push_event(encoder->srcpad, gst_event_new_eos()); } return ret; }
static gboolean gst_identity_event (GstBaseTransform * trans, GstEvent * event) { GstIdentity *identity; gboolean ret = TRUE; identity = GST_IDENTITY (trans); if (!identity->silent) { const GstStructure *s; gchar *sstr; GST_OBJECT_LOCK (identity); g_free (identity->last_message); 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: %d, %s) %p", GST_DEBUG_PAD_NAME (trans->sinkpad), 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_NEWSEGMENT)) { if (trans->have_newsegment == FALSE) { GstEvent *news; GstFormat format; gst_event_parse_new_segment (event, NULL, NULL, &format, NULL, NULL, NULL); /* This is the first newsegment, send out a (0, -1) newsegment */ news = gst_event_new_new_segment (TRUE, 1.0, format, 0, -1, 0); gst_pad_event_default (trans->sinkpad, 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_NEWSEGMENT) { identity->prev_timestamp = identity->prev_duration = GST_CLOCK_TIME_NONE; identity->prev_offset = identity->prev_offset_end = GST_BUFFER_OFFSET_NONE; } ret = parent_class->event (trans, event); if (identity->single_segment && (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT)) { /* eat up segments */ ret = FALSE; } return ret; }
static gboolean gst_type_find_element_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) { gboolean res = FALSE; GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (parent); GST_DEBUG_OBJECT (typefind, "got %s event in mode %d", GST_EVENT_TYPE_NAME (event), typefind->mode); switch (typefind->mode) { case MODE_TYPEFIND: switch (GST_EVENT_TYPE (event)) { case GST_EVENT_CAPS: { GstCaps *caps; /* Parse and push out our caps and data */ gst_event_parse_caps (event, &caps); res = gst_type_find_element_setcaps (typefind, caps); gst_event_unref (event); break; } case GST_EVENT_GAP: { GST_FIXME_OBJECT (typefind, "GAP events during typefinding not handled properly"); /* FIXME: These would need to be inserted in the stream at * the right position between buffers, but we combine all * buffers with a GstAdapter. Drop the GAP event for now, * which will only cause an implicit GAP between buffers. */ gst_event_unref (event); res = TRUE; break; } case GST_EVENT_EOS: { GST_INFO_OBJECT (typefind, "Got EOS and no type found yet"); gst_type_find_element_chain_do_typefinding (typefind, FALSE, TRUE); res = gst_pad_push_event (typefind->src, event); break; } case GST_EVENT_FLUSH_STOP:{ GList *l; GST_OBJECT_LOCK (typefind); for (l = typefind->cached_events; l; l = l->next) { if (GST_EVENT_IS_STICKY (l->data) && GST_EVENT_TYPE (l->data) != GST_EVENT_SEGMENT && GST_EVENT_TYPE (l->data) != GST_EVENT_EOS) { gst_pad_store_sticky_event (typefind->src, l->data); } gst_event_unref (l->data); } g_list_free (typefind->cached_events); typefind->cached_events = NULL; gst_adapter_clear (typefind->adapter); GST_OBJECT_UNLOCK (typefind); /* fall through */ } case GST_EVENT_FLUSH_START: res = gst_pad_push_event (typefind->src, event); break; default: /* Forward events that would happen before the caps event * directly instead of storing them. There's no reason not * to send them directly and we should only store events * for later sending that would need to come after the caps * event */ if (GST_EVENT_TYPE (event) < GST_EVENT_CAPS) { res = gst_pad_push_event (typefind->src, event); } else { GST_DEBUG_OBJECT (typefind, "Saving %s event to send later", GST_EVENT_TYPE_NAME (event)); GST_OBJECT_LOCK (typefind); typefind->cached_events = g_list_append (typefind->cached_events, event); GST_OBJECT_UNLOCK (typefind); res = TRUE; } break; } break; case MODE_NORMAL: res = gst_pad_push_event (typefind->src, event); break; case MODE_ERROR: break; default: g_assert_not_reached (); } return res; }
static gboolean gst_omx_audio_enc_sink_event (GstAudioEncoder * encoder, GstEvent * event) { GstOMXAudioEnc *self; GstOMXAudioEncClass *klass; OMX_ERRORTYPE err; self = GST_OMX_AUDIO_ENC (encoder); klass = GST_OMX_AUDIO_ENC_GET_CLASS (self); if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) { GstOMXBuffer *buf; GstOMXAcquireBufferReturn acq_ret; GST_DEBUG_OBJECT (self, "Sending EOS to the component"); /* Don't send EOS buffer twice, this doesn't work */ if (self->eos) { GST_DEBUG_OBJECT (self, "Component is already EOS"); return TRUE; } self->eos = TRUE; if ((klass->cdata.hacks & GST_OMX_HACK_NO_EMPTY_EOS_BUFFER)) { GST_WARNING_OBJECT (self, "Component does not support empty EOS buffers"); /* Insert a NULL into the queue to signal EOS */ g_mutex_lock (&self->enc->lock); g_queue_push_tail (&self->enc_out_port->pending_buffers, NULL); g_mutex_unlock (&self->enc->lock); g_mutex_lock (&self->enc->messages_lock); g_cond_broadcast (&self->enc->messages_cond); g_mutex_unlock (&self->enc->messages_lock); return TRUE; } /* Make sure to release the base class stream lock, otherwise * _loop() can't call _finish_frame() and we might block forever * because no input buffers are released */ GST_AUDIO_ENCODER_STREAM_UNLOCK (self); /* Send an EOS buffer to the component and let the base * class drop the EOS event. We will send it later when * the EOS buffer arrives on the output port. */ acq_ret = gst_omx_port_acquire_buffer (self->enc_in_port, &buf); if (acq_ret == GST_OMX_ACQUIRE_BUFFER_OK) { buf->omx_buf->nFilledLen = 0; buf->omx_buf->nTimeStamp = gst_util_uint64_scale (self->last_upstream_ts, OMX_TICKS_PER_SECOND, GST_SECOND); buf->omx_buf->nTickCount = 0; buf->omx_buf->nFlags |= OMX_BUFFERFLAG_EOS; err = gst_omx_port_release_buffer (self->enc_in_port, buf); if (err != OMX_ErrorNone) { GST_ERROR_OBJECT (self, "Failed to send EOS to component: %s (0x%08x)", gst_omx_error_to_string (err), err); } else { GST_DEBUG_OBJECT (self, "Sent EOS to the component"); } } else { GST_ERROR_OBJECT (self, "Failed to acquire buffer for EOS: %d", acq_ret); } GST_AUDIO_ENCODER_STREAM_LOCK (self); return TRUE; } return FALSE; }
static gboolean gst_raw_parse_sink_event (GstPad * pad, GstEvent * event) { GstRawParse *rp = GST_RAW_PARSE (gst_pad_get_parent (pad)); gboolean ret; switch (GST_EVENT_TYPE (event)) { case GST_EVENT_EOS: case GST_EVENT_FLUSH_STOP: /* Only happens in push mode */ gst_raw_parse_reset (rp); ret = gst_pad_push_event (rp->srcpad, event); break; case GST_EVENT_NEWSEGMENT: { GstClockTimeDiff start, stop, time; gdouble rate, arate; gboolean update; GstFormat format; /* Only happens in push mode */ gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, &start, &stop, &time); if (format == GST_FORMAT_TIME) { gst_segment_set_newsegment_full (&rp->segment, update, rate, arate, GST_FORMAT_TIME, start, stop, time); ret = gst_pad_push_event (rp->srcpad, event); } else { gst_event_unref (event); ret = gst_raw_parse_convert (rp, format, start, GST_FORMAT_TIME, &start); ret &= gst_raw_parse_convert (rp, format, time, GST_FORMAT_TIME, &time); ret &= gst_raw_parse_convert (rp, format, stop, GST_FORMAT_TIME, &stop); if (!ret) { GST_ERROR_OBJECT (rp, "Failed converting to GST_FORMAT_TIME format (%d)", format); break; } gst_segment_set_newsegment_full (&rp->segment, update, rate, arate, GST_FORMAT_TIME, start, stop, time); /* create new segment with the fields converted to time */ event = gst_event_new_new_segment_full (update, rate, arate, GST_FORMAT_TIME, start, stop, time); ret = gst_pad_push_event (rp->srcpad, event); } break; } default: ret = gst_pad_event_default (rp->sinkpad, event); break; } gst_object_unref (rp); return ret; }
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 = 0, stop = 0; 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_DEBUG_OBJECT (timidity, "received segment seek %d, %d", (gint) start_type, (gint) stop_type); } else { GST_DEBUG_OBJECT (timidity, "received normal seek %d", (gint) start_type); update = FALSE; } gst_pad_push_event (timidity->srcpad, gst_timidity_get_new_segment_event (timidity, GST_FORMAT_TIME, update)); timidity->o_seek = TRUE; gst_pad_start_task (timidity->sinkpad, (GstTaskFunction) gst_timidity_loop, timidity->sinkpad); GST_PAD_STREAM_UNLOCK (timidity->sinkpad); GST_DEBUG ("seek done"); } res = TRUE; break; default: break; } g_object_unref (timidity); return res; }
static gboolean gst_base_video_codec_sink_event (GstPad * pad, GstEvent * event) { GstBaseVideoCodec *base_video_codec; gboolean ret = FALSE; base_video_codec = GST_BASE_VIDEO_CODEC (gst_pad_get_parent (pad)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_FLUSH_START: ret = gst_pad_push_event (base_video_codec->srcpad, event); break; case GST_EVENT_FLUSH_STOP: gst_base_video_codec_reset (base_video_codec); ret = gst_pad_push_event (base_video_codec->srcpad, event); break; case GST_EVENT_EOS: if (gst_base_video_codec_push_all (base_video_codec, FALSE) == GST_FLOW_ERROR) { gst_event_unref (event); return FALSE; } ret = gst_pad_push_event (base_video_codec->srcpad, event); break; case GST_EVENT_NEWSEGMENT: { gboolean update; GstFormat format; gdouble rate; gint64 start, stop, time; gst_event_parse_new_segment (event, &update, &rate, &format, &start, &stop, &time); if (format != GST_FORMAT_TIME) goto newseg_wrong_format; if (rate <= 0.0) goto newseg_wrong_rate; GST_DEBUG ("newsegment %" GST_TIME_FORMAT " %" GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (time)); gst_segment_set_newsegment (&base_video_codec->state.segment, update, rate, format, start, stop, time); ret = gst_pad_push_event (base_video_codec->srcpad, event); break; } default: ret = gst_pad_push_event (base_video_codec->srcpad, event); break; } done: gst_object_unref (base_video_codec); return ret; newseg_wrong_format: GST_DEBUG_OBJECT (base_video_codec, "received non TIME newsegment"); gst_event_unref (event); goto done; newseg_wrong_rate: GST_DEBUG_OBJECT (base_video_codec, "negative rates not supported"); gst_event_unref (event); goto done; }
static gboolean gst_dvbsub_overlay_event_text (GstPad * pad, GstObject * parent, GstEvent * event) { gboolean ret = FALSE; GstDVBSubOverlay *render = GST_DVBSUB_OVERLAY (parent); GST_DEBUG_OBJECT (pad, "received text event %s", GST_EVENT_TYPE_NAME (event)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEGMENT: { GstSegment seg; GST_DEBUG_OBJECT (render, "received new segment"); gst_event_copy_segment (event, &seg); if (seg.format == GST_FORMAT_TIME) { GST_DEBUG_OBJECT (render, "SUBTITLE SEGMENT now: %" GST_SEGMENT_FORMAT, &render->subtitle_segment); render->subtitle_segment = seg; GST_DEBUG_OBJECT (render, "SUBTITLE SEGMENT after: %" GST_SEGMENT_FORMAT, &render->subtitle_segment); ret = TRUE; gst_event_unref (event); } else { GST_ELEMENT_WARNING (render, STREAM, MUX, (NULL), ("received non-TIME newsegment event on subtitle sinkpad")); ret = FALSE; gst_event_unref (event); } break; } case GST_EVENT_FLUSH_STOP: GST_DEBUG_OBJECT (render, "stop flushing"); gst_dvbsub_overlay_flush_subtitles (render); gst_segment_init (&render->subtitle_segment, GST_FORMAT_TIME); gst_event_unref (event); ret = TRUE; break; case GST_EVENT_FLUSH_START: GST_DEBUG_OBJECT (render, "begin flushing"); gst_event_unref (event); ret = TRUE; break; case GST_EVENT_EOS: GST_INFO_OBJECT (render, "text EOS"); gst_event_unref (event); ret = TRUE; break; case GST_EVENT_GAP: gst_event_unref (event); ret = TRUE; break; case GST_EVENT_CAPS: /* don't want to forward the subtitle caps */ gst_event_unref (event); ret = TRUE; break; default: ret = gst_pad_push_event (render->srcpad, event); break; } return ret; }
static gboolean gst_video_flip_src_event (GstBaseTransform * trans, GstEvent * event) { GstVideoFlip *vf = GST_VIDEO_FLIP (trans); gdouble new_x, new_y, x, y; GstStructure *structure; gboolean ret; GST_DEBUG_OBJECT (vf, "handling %s event", GST_EVENT_TYPE_NAME (event)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_NAVIGATION: event = GST_EVENT (gst_mini_object_make_writable (GST_MINI_OBJECT (event))); structure = (GstStructure *) gst_event_get_structure (event); if (gst_structure_get_double (structure, "pointer_x", &x) && gst_structure_get_double (structure, "pointer_y", &y)) { GST_DEBUG_OBJECT (vf, "converting %fx%f", x, y); switch (vf->method) { case GST_VIDEO_FLIP_METHOD_90R: new_x = y; new_y = vf->to_width - x; break; case GST_VIDEO_FLIP_METHOD_90L: new_x = vf->to_height - y; new_y = x; break; case GST_VIDEO_FLIP_METHOD_OTHER: new_x = vf->to_height - y; new_y = vf->to_width - x; break; case GST_VIDEO_FLIP_METHOD_TRANS: new_x = y; new_y = x; break; case GST_VIDEO_FLIP_METHOD_180: new_x = vf->to_width - x; new_y = vf->to_height - y; break; case GST_VIDEO_FLIP_METHOD_HORIZ: new_x = vf->to_width - x; new_y = y; break; case GST_VIDEO_FLIP_METHOD_VERT: new_x = x; new_y = vf->to_height - y; break; default: new_x = x; new_y = y; break; } GST_DEBUG_OBJECT (vf, "to %fx%f", new_x, new_y); gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, new_x, "pointer_y", G_TYPE_DOUBLE, new_y, NULL); } break; default: break; } ret = GST_BASE_TRANSFORM_CLASS (parent_class)->src_event (trans, event); return ret; }
static gboolean pad_event (GstPad *pad, GstEvent *event) { GstOmxBaseFilter *self; GOmxCore *gomx; GOmxPort *in_port; GOmxPort *out_port; gboolean ret; self = GST_OMX_BASE_FILTER (GST_OBJECT_PARENT (pad)); gomx = self->gomx; in_port = self->in_port; out_port = self->out_port; GST_LOG_OBJECT (self, "begin"); GST_INFO_OBJECT (self, "event: %s", GST_EVENT_TYPE_NAME (event)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_EOS: if (self->initialized) { /* send buffer with eos flag */ /** @todo move to util */ { OMX_BUFFERHEADERTYPE *omx_buffer; GST_LOG_OBJECT (self, "request buffer"); omx_buffer = g_omx_port_request_buffer (in_port); if (G_LIKELY (omx_buffer)) { omx_buffer->nFlags |= OMX_BUFFERFLAG_EOS; GST_LOG_OBJECT (self, "release_buffer"); /* foo_buffer_untaint (omx_buffer); */ g_omx_port_release_buffer (in_port, omx_buffer); } else { g_omx_core_set_done (gomx); } } /* Wait for the output port to get the EOS. */ g_omx_core_wait_for_done (gomx); } ret = gst_pad_push_event (self->srcpad, event); break; case GST_EVENT_FLUSH_START: gst_pad_push_event (self->srcpad, event); self->last_pad_push_return = GST_FLOW_WRONG_STATE; g_omx_core_flush_start (gomx); gst_pad_pause_task (self->srcpad); ret = TRUE; break; case GST_EVENT_FLUSH_STOP: gst_pad_push_event (self->srcpad, event); self->last_pad_push_return = GST_FLOW_OK; g_omx_core_flush_stop (gomx); gst_pad_start_task (self->srcpad, output_loop, self->srcpad); ret = TRUE; break; case GST_EVENT_NEWSEGMENT: ret = gst_pad_push_event (self->srcpad, event); break; default: ret = gst_pad_push_event (self->srcpad, event); break; } GST_LOG_OBJECT (self, "end"); return ret; }
static GstPadProbeReturn link_to_videomixer (GstPad * pad, GstPadProbeInfo * info, KmsCompositeMixerData * data) { GstPadTemplate *sink_pad_template; KmsCompositeMixer *mixer; if (GST_EVENT_TYPE (GST_PAD_PROBE_INFO_EVENT (info)) != GST_EVENT_STREAM_START) { return GST_PAD_PROBE_PASS; } mixer = KMS_COMPOSITE_MIXER (data->mixer); GST_DEBUG ("stream start detected %d", data->id); KMS_COMPOSITE_MIXER_LOCK (mixer); data->link_probe_id = 0; sink_pad_template = gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (mixer->priv-> videomixer), "sink_%u"); if (G_UNLIKELY (sink_pad_template == NULL)) { GST_ERROR_OBJECT (mixer, "Error taking a new pad from videomixer"); KMS_COMPOSITE_MIXER_UNLOCK (mixer); return GST_PAD_PROBE_DROP; } if (mixer->priv->videotestsrc == NULL) { GstElement *capsfilter; GstCaps *filtercaps; GstPad *pad; mixer->priv->videotestsrc = gst_element_factory_make ("videotestsrc", NULL); capsfilter = gst_element_factory_make ("capsfilter", NULL); g_object_set (mixer->priv->videotestsrc, "is-live", TRUE, "pattern", /*black */ 2, NULL); filtercaps = gst_caps_new_simple ("video/x-raw", "format", G_TYPE_STRING, "AYUV", "width", G_TYPE_INT, mixer->priv->output_width, "height", G_TYPE_INT, mixer->priv->output_height, "framerate", GST_TYPE_FRACTION, 15, 1, NULL); g_object_set (G_OBJECT (capsfilter), "caps", filtercaps, NULL); gst_caps_unref (filtercaps); gst_bin_add_many (GST_BIN (mixer), mixer->priv->videotestsrc, capsfilter, NULL); gst_element_link (mixer->priv->videotestsrc, capsfilter); /*link capsfilter -> videomixer */ pad = gst_element_request_pad (mixer->priv->videomixer, sink_pad_template, NULL, NULL); gst_element_link_pads (capsfilter, NULL, mixer->priv->videomixer, GST_OBJECT_NAME (pad)); g_object_set (pad, "xpos", 0, "ypos", 0, "alpha", 0.0, NULL); g_object_unref (pad); gst_element_sync_state_with_parent (capsfilter); gst_element_sync_state_with_parent (mixer->priv->videotestsrc); } data->videoscale = gst_element_factory_make ("videoscale", NULL); data->capsfilter = gst_element_factory_make ("capsfilter", NULL); data->videorate = gst_element_factory_make ("videorate", NULL); data->queue = gst_element_factory_make ("queue", NULL); data->input = TRUE; gst_bin_add_many (GST_BIN (mixer), data->queue, data->videorate, data->videoscale, data->capsfilter, NULL); g_object_set (data->videorate, "average-period", 200 * GST_MSECOND, NULL); g_object_set (data->queue, "flush-on-eos", TRUE, "max-size-buffers", 60, NULL); gst_element_link_many (data->videorate, data->queue, data->videoscale, data->capsfilter, NULL); /*link capsfilter -> videomixer */ data->video_mixer_pad = gst_element_request_pad (mixer->priv->videomixer, sink_pad_template, NULL, NULL); gst_element_link_pads (data->capsfilter, NULL, mixer->priv->videomixer, GST_OBJECT_NAME (data->video_mixer_pad)); gst_element_link (data->videoconvert, data->videorate); data->probe_id = gst_pad_add_probe (data->video_mixer_pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, (GstPadProbeCallback) cb_EOS_received, KMS_COMPOSITE_MIXER_REF (data), (GDestroyNotify) kms_ref_struct_unref); gst_element_sync_state_with_parent (data->videoscale); gst_element_sync_state_with_parent (data->capsfilter); gst_element_sync_state_with_parent (data->videorate); gst_element_sync_state_with_parent (data->queue); /*recalculate the output sizes */ mixer->priv->n_elems++; kms_composite_mixer_recalculate_sizes (mixer); KMS_COMPOSITE_MIXER_UNLOCK (mixer); return GST_PAD_PROBE_REMOVE; }
/* srcpad functions */ static gboolean gst_stream_synchronizer_src_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_QOS:{ gdouble proportion; GstClockTimeDiff diff; GstClockTime timestamp; gint64 running_time_diff = -1; GstSyncStream *stream; gst_event_parse_qos (event, NULL, &proportion, &diff, ×tamp); gst_event_unref (event); GST_STREAM_SYNCHRONIZER_LOCK (self); stream = gst_pad_get_element_private (pad); if (stream) running_time_diff = stream->segment.base; GST_STREAM_SYNCHRONIZER_UNLOCK (self); if (running_time_diff == -1) { GST_WARNING_OBJECT (pad, "QOS event before group start"); goto out; } if (timestamp < running_time_diff) { GST_DEBUG_OBJECT (pad, "QOS event from previous group"); goto out; } GST_LOG_OBJECT (pad, "Adjusting QOS event: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT " = %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp), GST_TIME_ARGS (running_time_diff), GST_TIME_ARGS (timestamp - running_time_diff)); timestamp -= running_time_diff; /* That case is invalid for QoS events */ if (diff < 0 && -diff > timestamp) { GST_DEBUG_OBJECT (pad, "QOS event from previous group"); ret = TRUE; goto out; } event = gst_event_new_qos (GST_QOS_TYPE_UNDERFLOW, proportion, diff, timestamp); break; } default: break; } ret = gst_pad_event_default (pad, parent, event); out: return ret; }
static GstPadProbeReturn handle_mq_output (GstPad * pad, GstPadProbeInfo * info, MqStreamCtx * ctx) { GstSplitMuxSink *splitmux = ctx->splitmux; MqStreamBuf *buf_info = NULL; GST_LOG_OBJECT (pad, "Fired probe type 0x%x\n", info->type); /* FIXME: Handle buffer lists, until then make it clear they won't work */ if (info->type & GST_PAD_PROBE_TYPE_BUFFER_LIST) { g_warning ("Buffer list handling not implemented"); return GST_PAD_PROBE_DROP; } if (info->type & GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM) { GstEvent *event = gst_pad_probe_info_get_event (info); GST_LOG_OBJECT (pad, "Event %" GST_PTR_FORMAT, event); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEGMENT: gst_event_copy_segment (event, &ctx->out_segment); break; case GST_EVENT_FLUSH_STOP: GST_SPLITMUX_LOCK (splitmux); gst_segment_init (&ctx->out_segment, GST_FORMAT_UNDEFINED); g_queue_foreach (&ctx->queued_bufs, (GFunc) mq_stream_buf_free, NULL); g_queue_clear (&ctx->queued_bufs); ctx->flushing = FALSE; GST_SPLITMUX_UNLOCK (splitmux); break; case GST_EVENT_FLUSH_START: GST_SPLITMUX_LOCK (splitmux); GST_LOG_OBJECT (pad, "Flush start"); ctx->flushing = TRUE; GST_SPLITMUX_BROADCAST (splitmux); GST_SPLITMUX_UNLOCK (splitmux); break; case GST_EVENT_EOS: GST_SPLITMUX_LOCK (splitmux); if (splitmux->state == SPLITMUX_STATE_STOPPED) goto beach; ctx->out_eos = TRUE; GST_SPLITMUX_UNLOCK (splitmux); break; case GST_EVENT_GAP:{ GstClockTime gap_ts; gst_event_parse_gap (event, &gap_ts, NULL); if (gap_ts == GST_CLOCK_TIME_NONE) break; GST_SPLITMUX_LOCK (splitmux); gap_ts = gst_segment_to_running_time (&ctx->out_segment, GST_FORMAT_TIME, gap_ts); GST_LOG_OBJECT (pad, "Have GAP w/ ts %" GST_TIME_FORMAT, GST_TIME_ARGS (gap_ts)); if (splitmux->state == SPLITMUX_STATE_STOPPED) goto beach; ctx->out_running_time = gap_ts; complete_or_wait_on_out (splitmux, ctx); GST_SPLITMUX_UNLOCK (splitmux); break; } default: break; } return GST_PAD_PROBE_PASS; } /* Allow everything through until the configured next stopping point */ GST_SPLITMUX_LOCK (splitmux); buf_info = g_queue_pop_tail (&ctx->queued_bufs); if (buf_info == NULL) /* Can only happen due to a poorly timed flush */ goto beach; /* If we have popped a keyframe, decrement the queued_gop count */ if (buf_info->keyframe && splitmux->queued_gops > 0) splitmux->queued_gops--; ctx->out_running_time = buf_info->run_ts; GST_LOG_OBJECT (splitmux, "Pad %" GST_PTR_FORMAT " buffer with TS %" GST_TIME_FORMAT " size %" G_GSIZE_FORMAT, pad, GST_TIME_ARGS (ctx->out_running_time), buf_info->buf_size); complete_or_wait_on_out (splitmux, ctx); if (splitmux->muxed_out_time == GST_CLOCK_TIME_NONE || splitmux->muxed_out_time < buf_info->run_ts) splitmux->muxed_out_time = buf_info->run_ts; splitmux->muxed_out_bytes += buf_info->buf_size; #ifndef GST_DISABLE_GST_DEBUG { GstBuffer *buf = gst_pad_probe_info_get_buffer (info); GST_LOG_OBJECT (pad, "Returning to pass buffer %" GST_PTR_FORMAT " run ts %" GST_TIME_FORMAT, buf, GST_TIME_ARGS (ctx->out_running_time)); } #endif GST_SPLITMUX_UNLOCK (splitmux); mq_stream_buf_free (buf_info); return GST_PAD_PROBE_PASS; beach: GST_SPLITMUX_UNLOCK (splitmux); return GST_PAD_PROBE_DROP; }
/* 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) { if (ostream->segment.rate > 0) stop_running_time = gst_segment_to_running_time (&ostream->segment, GST_FORMAT_TIME, ostream->segment.stop); else stop_running_time = gst_segment_to_running_time (&ostream->segment, GST_FORMAT_TIME, ostream->segment.start); 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); if (ostream->segment.rate > 0) position_running_time -= gst_segment_to_running_time (&ostream->segment, GST_FORMAT_TIME, ostream->segment.start); else position_running_time -= gst_segment_to_running_time (&ostream->segment, GST_FORMAT_TIME, ostream->segment.stop); 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) { if (ostream->segment.rate > 0) start_running_time = gst_segment_to_running_time (&ostream->segment, GST_FORMAT_TIME, ostream->segment.start); else start_running_time = gst_segment_to_running_time (&ostream->segment, GST_FORMAT_TIME, ostream->segment.stop); 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; }
/* Pipeline Callbacks */ static gboolean probe_cb (InsanityGstTest * ptest, GstPad * pad, GstMiniObject * object, gpointer userdata) { InsanityTest *test = INSANITY_TEST (ptest); global_last_probe = g_get_monotonic_time (); DECODER_TEST_LOCK (); if (GST_IS_BUFFER (object)) { GstBuffer *buf; GstClockTime ts; buf = GST_BUFFER (object); ts = GST_BUFFER_PTS (buf); /* First check clipping */ if (glob_testing_parser == FALSE && GST_CLOCK_TIME_IS_VALID (ts) && glob_waiting_segment == FALSE) { GstClockTime ts_end, cstart, cstop; /* Check if buffer is completely outside the segment */ ts_end = ts; if (GST_BUFFER_DURATION_IS_VALID (buf)) ts_end += GST_BUFFER_DURATION (buf); /* Check if buffer is completely outside the segment */ ts_end = ts; if (!gst_segment_clip (&glob_last_segment, glob_last_segment.format, ts, ts_end, &cstart, &cstop)) { char *msg = g_strdup_printf ("Got timestamp %" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT ", outside configured segment (%" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT "), method %s", GST_TIME_ARGS (ts), GST_TIME_ARGS (ts_end), GST_TIME_ARGS (glob_last_segment.start), GST_TIME_ARGS (glob_last_segment.stop), test_get_name (glob_in_progress)); insanity_test_validate_checklist_item (INSANITY_TEST (ptest), "segment-clipping", FALSE, msg); g_free (msg); glob_bad_segment_clipping = TRUE; } } switch (glob_in_progress) { case TEST_NONE: if (glob_waiting_first_segment == TRUE) insanity_test_validate_checklist_item (test, "first-segment", FALSE, "Got a buffer before the first segment"); /* Got the first buffer, starting testing dance */ next_test (test); break; case TEST_POSITION: test_position (test, buf); break; case TEST_FAST_FORWARD: case TEST_BACKWARD_PLAYBACK: case TEST_FAST_BACKWARD: { gint64 stime_ts; if (GST_CLOCK_TIME_IS_VALID (ts) == FALSE || glob_waiting_segment == TRUE) { break; } stime_ts = gst_segment_to_stream_time (&glob_last_segment, glob_last_segment.format, ts); if (GST_CLOCK_TIME_IS_VALID (glob_seek_first_buf_ts) == FALSE) { GstClockTime expected_ts = gst_segment_to_stream_time (&glob_last_segment, glob_last_segment.format, glob_seek_rate < 0 ? glob_seek_stop_ts : glob_seek_segment_seektime); GstClockTimeDiff diff = ABS (GST_CLOCK_DIFF (stime_ts, expected_ts)); if (diff > SEEK_THRESHOLD) { gchar *valmsg = g_strdup_printf ("Received buffer timestamp %" GST_TIME_FORMAT " Seeek wanted %" GST_TIME_FORMAT "", GST_TIME_ARGS (stime_ts), GST_TIME_ARGS (expected_ts)); validate_current_test (test, FALSE, valmsg); next_test (test); g_free (valmsg); } else glob_seek_first_buf_ts = stime_ts; } else { GstClockTimeDiff diff = GST_CLOCK_DIFF (stime_ts, glob_seek_first_buf_ts); if (diff < 0) diff = -diff; if (diff >= glob_playback_duration * GST_SECOND) { validate_current_test (test, TRUE, NULL); next_test (test); } } break; } default: break; } } else if (GST_IS_EVENT (object)) { GstEvent *event = GST_EVENT (object); guint seqnum = gst_event_get_seqnum (event); if (G_LIKELY (glob_seqnum_found == FALSE) && seqnum == glob_seqnum) glob_seqnum_found = TRUE; if (glob_seqnum_found == TRUE && seqnum != glob_seqnum) { gchar *message = g_strdup_printf ("Current seqnum %i != " "received %i", glob_seqnum, seqnum); insanity_test_validate_checklist_item (test, "seqnum-management", FALSE, message); glob_wrong_seqnum = TRUE; g_free (message); } switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEGMENT: { gst_event_copy_segment (event, &glob_last_segment); if (glob_waiting_segment == FALSE) /* Cache the segment as it will be our reference but don't look * further */ goto done; glob_last_segment_start_time = glob_last_segment.start; if (glob_waiting_first_segment == TRUE) { insanity_test_validate_checklist_item (test, "first-segment", TRUE, NULL); glob_waiting_first_segment = FALSE; } else if (glob_in_progress >= TEST_FAST_FORWARD && glob_in_progress <= TEST_FAST_BACKWARD) { GstClockTimeDiff diff; gboolean valid_stop = TRUE; GstClockTimeDiff wdiff, rdiff; rdiff = ABS (GST_CLOCK_DIFF (glob_last_segment.stop, glob_last_segment.start)) * ABS (glob_last_segment.rate * glob_last_segment.applied_rate); wdiff = ABS (GST_CLOCK_DIFF (glob_seek_stop_ts, glob_seek_segment_seektime)); diff = GST_CLOCK_DIFF (glob_last_segment.position, glob_seek_segment_seektime); if (diff < 0) diff = -diff; /* Now compare with the expected segment */ if ((glob_last_segment.rate * glob_last_segment.applied_rate) == glob_seek_rate && diff <= SEEK_THRESHOLD && valid_stop) { glob_seek_got_segment = TRUE; } else { GstClockTime stopdiff = ABS (GST_CLOCK_DIFF (rdiff, wdiff)); gchar *validate_msg = g_strdup_printf ("Wrong segment received, Rate %f expected " "%f, start time diff %" GST_TIME_FORMAT " stop diff %" GST_TIME_FORMAT, (glob_last_segment.rate * glob_last_segment.applied_rate), glob_seek_rate, GST_TIME_ARGS (diff), GST_TIME_ARGS (stopdiff)); validate_current_test (test, FALSE, validate_msg); next_test (test); g_free (validate_msg); } } glob_waiting_segment = FALSE; break; } default: break; } } done: DECODER_TEST_UNLOCK (); return TRUE; }
static gboolean gst_kate_enc_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) { GstKateEnc *ke = GST_KATE_ENC (parent); const GstStructure *structure; gboolean ret; switch (GST_EVENT_TYPE (event)) { case GST_EVENT_CAPS: { GstCaps *caps; gst_event_parse_caps (event, &caps); ret = gst_kate_enc_setcaps (ke, caps); gst_event_unref (event); break; } case GST_EVENT_SEGMENT:{ GstSegment seg; GST_LOG_OBJECT (ke, "Got newsegment event"); gst_event_copy_segment (event, &seg); if (!ke->headers_sent) { if (ke->pending_segment) gst_event_unref (ke->pending_segment); ke->pending_segment = event; event = NULL; } if (ke->initialized) { GST_LOG_OBJECT (ke, "ensuring all headers are in"); if (gst_kate_enc_flush_headers (ke) != GST_FLOW_OK) { GST_WARNING_OBJECT (ke, "Failed to flush headers"); } else { if (seg.format != GST_FORMAT_TIME || !GST_CLOCK_TIME_IS_VALID (seg.start)) { GST_WARNING_OBJECT (ke, "No time in newsegment event %p, format %d, timestamp %" G_GINT64_FORMAT, event, (int) seg.format, seg.start); /* to be safe, we'd need to generate a keepalive anyway, but we'd have to guess at the timestamp to use; a good guess would be the last known timestamp plus the keepalive time, but if we then get a packet with a timestamp less than this, it would fail to encode, which would be Bad. If we don't encode a keepalive, we run the risk of stalling the pipeline and hanging, which is Very Bad. Oh dear. We can't exit(-1), can we ? */ } else { float t = seg.start / (double) GST_SECOND; if (ke->delayed_spu && t - ke->delayed_start / (double) GST_SECOND >= ke->default_spu_duration) { if (G_UNLIKELY (gst_kate_enc_flush_waiting (ke, seg.start) != GST_FLOW_OK)) { GST_WARNING_OBJECT (ke, "Failed to encode delayed packet"); /* continue with new segment handling anyway */ } } GST_LOG_OBJECT (ke, "ts %f, last %f (min %f)", t, ke->last_timestamp / (double) GST_SECOND, ke->keepalive_min_time); if (ke->keepalive_min_time > 0.0f && t - ke->last_timestamp / (double) GST_SECOND >= ke->keepalive_min_time) { /* we only generate a keepalive if there is no SPU waiting, as it would mean out of sequence start times - and granulepos */ if (!ke->delayed_spu) { gst_kate_enc_generate_keepalive (ke, seg.start); } } } } } if (event) ret = gst_pad_push_event (ke->srcpad, event); else ret = TRUE; break; } case GST_EVENT_CUSTOM_DOWNSTREAM: GST_LOG_OBJECT (ke, "Got custom downstream event"); /* adapted from the dvdsubdec element */ structure = gst_event_get_structure (event); if (structure != NULL && gst_structure_has_name (structure, "application/x-gst-dvd")) { if (ke->initialized) { GST_LOG_OBJECT (ke, "ensuring all headers are in"); if (gst_kate_enc_flush_headers (ke) != GST_FLOW_OK) { GST_WARNING_OBJECT (ke, "Failed to flush headers"); } else { const gchar *event_name = gst_structure_get_string (structure, "event"); if (event_name) { if (!strcmp (event_name, "dvd-spu-clut-change")) { gchar name[16]; int idx; gboolean found; gint value; GST_INFO_OBJECT (ke, "New CLUT received"); for (idx = 0; idx < 16; ++idx) { g_snprintf (name, sizeof (name), "clut%02d", idx); found = gst_structure_get_int (structure, name, &value); if (found) { ke->spu_clut[idx] = value; } else { GST_WARNING_OBJECT (ke, "DVD CLUT event did not contain %s field", name); } } } else if (!strcmp (event_name, "dvd-lang-codes")) { /* we can't know which stream corresponds to us */ } } else { GST_WARNING_OBJECT (ke, "custom downstream event with no name"); } } } } ret = gst_pad_push_event (ke->srcpad, event); break; case GST_EVENT_TAG: GST_LOG_OBJECT (ke, "Got tag event"); if (ke->tags) { GstTagList *list; gst_event_parse_tag (event, &list); gst_tag_list_insert (ke->tags, list, gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (ke))); } else { g_assert_not_reached (); } ret = gst_pad_event_default (pad, parent, event); break; case GST_EVENT_EOS: GST_INFO_OBJECT (ke, "Got EOS event"); if (ke->initialized) { GST_LOG_OBJECT (ke, "ensuring all headers are in"); if (gst_kate_enc_flush_headers (ke) != GST_FLOW_OK) { GST_WARNING_OBJECT (ke, "Failed to flush headers"); } else { kate_packet kp; int ret; GstClockTime delayed_end = ke->delayed_start + ke->default_spu_duration * GST_SECOND; if (G_UNLIKELY (gst_kate_enc_flush_waiting (ke, delayed_end) != GST_FLOW_OK)) { GST_WARNING_OBJECT (ke, "Failed to encode delayed packet"); /* continue with EOS handling anyway */ } ret = kate_encode_finish (&ke->k, -1, &kp); if (ret < 0) { GST_WARNING_OBJECT (ke, "Failed to encode EOS packet: %s", gst_kate_util_get_error_message (ret)); } else { kate_int64_t granpos = kate_encode_get_granule (&ke->k); GST_LOG_OBJECT (ke, "EOS packet encoded"); if (gst_kate_enc_push_and_free_kate_packet (ke, &kp, granpos, ke->latest_end_time, 0, FALSE)) { GST_WARNING_OBJECT (ke, "Failed to push EOS packet"); } } } } ret = gst_pad_event_default (pad, parent, event); break; default: GST_LOG_OBJECT (ke, "Got unhandled event"); ret = gst_pad_event_default (pad, parent, event); break; } return ret; }