static GstPadProbeReturn gst_hls_sink_ghost_buffer_probe (GstPad * pad, GstPadProbeInfo * info, gpointer data) { GstHlsSink *sink = GST_HLS_SINK_CAST (data); GstBuffer *buffer = gst_pad_probe_info_get_buffer (info); if (sink->target_duration == 0 || sink->waiting_fku) return GST_PAD_PROBE_OK; gst_hls_sink_check_schedule_next_key_unit (sink, buffer); return GST_PAD_PROBE_OK; }
static GstPadProbeReturn handle_output (GstPad * pad, GstPadProbeInfo * info, StreamInfo * si) { GstClockTime start, end; GstBuffer *buf; GST_LOG_OBJECT (pad, "Fired probe type 0x%x", info->type); 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, &si->seg); break; case GST_EVENT_EOS: dump_times (si); break; default: break; } return GST_PAD_PROBE_PASS; } buf = gst_pad_probe_info_get_buffer (info); if (!GST_BUFFER_PTS_IS_VALID (buf)) goto done; end = start = GST_BUFFER_PTS (buf); if (GST_BUFFER_DURATION_IS_VALID (buf)) end += GST_BUFFER_DURATION (buf); gst_segment_clip (&si->seg, GST_FORMAT_TIME, start, end, &start, &end); start = gst_segment_to_stream_time (&si->seg, GST_FORMAT_TIME, start); end = gst_segment_to_stream_time (&si->seg, GST_FORMAT_TIME, end); GST_DEBUG_OBJECT (pad, "new buffer %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (end)); /* Now extend measured time range to include new times */ extend_times (si, start, end); done: return GST_PAD_PROBE_PASS; }
static GstPadProbeReturn gst_hls_sink_ghost_buffer_probe (GstPad * pad, GstPadProbeInfo * info, gpointer data) { GstHlsSink *sink = GST_HLS_SINK_CAST (data); GstBuffer *buffer = gst_pad_probe_info_get_buffer (info); GstClockTime timestamp; timestamp = GST_BUFFER_TIMESTAMP (buffer); if (sink->target_duration == 0 || !GST_CLOCK_TIME_IS_VALID (timestamp) || sink->waiting_fku) return GST_PAD_PROBE_OK; sink->last_running_time = gst_segment_to_running_time (&sink->segment, GST_FORMAT_TIME, timestamp); schedule_next_key_unit (sink); return GST_PAD_PROBE_OK; }
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 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; }
GstPadProbeReturn GstEnginePipeline::HandoffCallback(GstPad*, GstPadProbeInfo* info, gpointer self) { GstEnginePipeline* instance = reinterpret_cast<GstEnginePipeline*>(self); GstBuffer* buf = gst_pad_probe_info_get_buffer(info); QList<BufferConsumer*> consumers; { QMutexLocker l(&instance->buffer_consumers_mutex_); consumers = instance->buffer_consumers_; } for (BufferConsumer* consumer : consumers) { gst_buffer_ref(buf); consumer->ConsumeBuffer(buf, instance->id()); } // Calculate the end time of this buffer so we can stop playback if it's // after the end time of this song. if (instance->end_offset_nanosec_ > 0) { quint64 start_time = GST_BUFFER_TIMESTAMP(buf) - instance->segment_start_; quint64 duration = GST_BUFFER_DURATION(buf); quint64 end_time = start_time + duration; if (end_time > instance->end_offset_nanosec_) { if (instance->has_next_valid_url()) { if (instance->next_url_ == instance->url_ && instance->next_beginning_offset_nanosec_ == instance->end_offset_nanosec_) { // The "next" song is actually the next segment of this file - so // cheat and keep on playing, but just tell the Engine we've moved on. instance->end_offset_nanosec_ = instance->next_end_offset_nanosec_; instance->next_url_ = QUrl(); instance->next_beginning_offset_nanosec_ = 0; instance->next_end_offset_nanosec_ = 0; // GstEngine will try to seek to the start of the new section, but // we're already there so ignore it. instance->ignore_next_seek_ = true; emit instance->EndOfStreamReached(instance->id(), true); } else { // We have a next song but we can't cheat, so move to it normally. instance->TransitionToNext(); } } else { // There's no next song emit instance->EndOfStreamReached(instance->id(), false); } } } if (instance->emit_track_ended_on_time_discontinuity_) { if (GST_BUFFER_FLAG_IS_SET(buf, GST_BUFFER_FLAG_DISCONT) || GST_BUFFER_OFFSET(buf) < instance->last_buffer_offset_) { qLog(Debug) << "Buffer discontinuity - emitting EOS"; instance->emit_track_ended_on_time_discontinuity_ = false; emit instance->EndOfStreamReached(instance->id(), true); } } instance->last_buffer_offset_ = GST_BUFFER_OFFSET(buf); return GST_PAD_PROBE_OK; }