static GstFlowReturn gst_dvbsub_overlay_chain_video (GstPad * pad, GstObject * parent, GstBuffer * buffer) { GstDVBSubOverlay *overlay = GST_DVBSUB_OVERLAY (parent); GstFlowReturn ret = GST_FLOW_OK; gint64 start, stop; guint64 cstart, cstop; gboolean in_seg; GstClockTime vid_running_time, vid_running_time_end; if (GST_VIDEO_INFO_FORMAT (&overlay->info) == GST_VIDEO_FORMAT_UNKNOWN) return GST_FLOW_NOT_NEGOTIATED; if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) goto missing_timestamp; start = GST_BUFFER_TIMESTAMP (buffer); GST_LOG_OBJECT (overlay, "Video segment: %" GST_SEGMENT_FORMAT " --- Subtitle position: %" GST_TIME_FORMAT " --- BUFFER: ts=%" GST_TIME_FORMAT, &overlay->video_segment, GST_TIME_ARGS (overlay->subtitle_segment.position), GST_TIME_ARGS (start)); /* ignore buffers that are outside of the current segment */ if (!GST_BUFFER_DURATION_IS_VALID (buffer)) { stop = GST_CLOCK_TIME_NONE; } else { stop = start + GST_BUFFER_DURATION (buffer); } in_seg = gst_segment_clip (&overlay->video_segment, GST_FORMAT_TIME, start, stop, &cstart, &cstop); if (!in_seg) { GST_DEBUG_OBJECT (overlay, "Buffer outside configured segment -- dropping"); gst_buffer_unref (buffer); return GST_FLOW_OK; } buffer = gst_buffer_make_writable (buffer); GST_BUFFER_TIMESTAMP (buffer) = cstart; if (GST_BUFFER_DURATION_IS_VALID (buffer)) GST_BUFFER_DURATION (buffer) = cstop - cstart; vid_running_time = gst_segment_to_running_time (&overlay->video_segment, GST_FORMAT_TIME, cstart); if (GST_BUFFER_DURATION_IS_VALID (buffer)) vid_running_time_end = gst_segment_to_running_time (&overlay->video_segment, GST_FORMAT_TIME, cstop); else vid_running_time_end = vid_running_time; GST_DEBUG_OBJECT (overlay, "Video running time: %" GST_TIME_FORMAT, GST_TIME_ARGS (vid_running_time)); overlay->video_segment.position = GST_BUFFER_TIMESTAMP (buffer); g_mutex_lock (&overlay->dvbsub_mutex); if (!g_queue_is_empty (overlay->pending_subtitles)) { DVBSubtitles *tmp, *candidate = NULL; while (!g_queue_is_empty (overlay->pending_subtitles)) { tmp = g_queue_peek_head (overlay->pending_subtitles); if (tmp->pts > vid_running_time_end) { /* For a future video frame */ break; } else if (tmp->num_rects == 0) { /* Clear screen */ if (overlay->current_subtitle) dvb_subtitles_free (overlay->current_subtitle); overlay->current_subtitle = NULL; if (candidate) dvb_subtitles_free (candidate); candidate = NULL; g_queue_pop_head (overlay->pending_subtitles); dvb_subtitles_free (tmp); tmp = NULL; } else if (tmp->pts + tmp->page_time_out * GST_SECOND * ABS (overlay->subtitle_segment.rate) >= vid_running_time) { if (candidate) dvb_subtitles_free (candidate); candidate = tmp; g_queue_pop_head (overlay->pending_subtitles); } else { /* Too late */ dvb_subtitles_free (tmp); tmp = NULL; g_queue_pop_head (overlay->pending_subtitles); } } if (candidate) { GST_DEBUG_OBJECT (overlay, "Time to show the next subtitle page (%" GST_TIME_FORMAT " >= %" GST_TIME_FORMAT ") - it has %u regions", GST_TIME_ARGS (vid_running_time), GST_TIME_ARGS (candidate->pts), candidate->num_rects); dvb_subtitles_free (overlay->current_subtitle); overlay->current_subtitle = candidate; if (overlay->current_comp) gst_video_overlay_composition_unref (overlay->current_comp); overlay->current_comp = gst_dvbsub_overlay_subs_to_comp (overlay, overlay->current_subtitle); } } /* Check that we haven't hit the fallback timeout for current subtitle page */ if (overlay->current_subtitle && vid_running_time > (overlay->current_subtitle->pts + overlay->current_subtitle->page_time_out * GST_SECOND * ABS (overlay->subtitle_segment.rate))) { GST_INFO_OBJECT (overlay, "Subtitle page not redefined before fallback page_time_out of %u seconds (missed data?) - deleting current page", overlay->current_subtitle->page_time_out); dvb_subtitles_free (overlay->current_subtitle); overlay->current_subtitle = NULL; } /* Now render it */ if (g_atomic_int_get (&overlay->enable) && overlay->current_subtitle) { GstVideoFrame frame; g_assert (overlay->current_comp); if (overlay->attach_compo_to_buffer) { GST_DEBUG_OBJECT (overlay, "Attaching overlay image to video buffer"); gst_buffer_add_video_overlay_composition_meta (buffer, overlay->current_comp); } else { GST_DEBUG_OBJECT (overlay, "Blending overlay image to video buffer"); gst_video_frame_map (&frame, &overlay->info, buffer, GST_MAP_READWRITE); gst_video_overlay_composition_blend (overlay->current_comp, &frame); gst_video_frame_unmap (&frame); } } g_mutex_unlock (&overlay->dvbsub_mutex); ret = gst_pad_push (overlay->srcpad, buffer); return ret; missing_timestamp: { GST_WARNING_OBJECT (overlay, "video buffer without timestamp, discarding"); gst_buffer_unref (buffer); return GST_FLOW_OK; } }
static GstFlowReturn gst_overlay_composition_sink_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) { GstOverlayComposition *self = GST_OVERLAY_COMPOSITION (parent); GstVideoOverlayComposition *compo = NULL; GstVideoOverlayCompositionMeta *upstream_compo_meta; if (gst_pad_check_reconfigure (self->srcpad)) { if (!gst_overlay_composition_negotiate (self, NULL)) { gst_pad_mark_reconfigure (self->srcpad); gst_buffer_unref (buffer); GST_OBJECT_LOCK (self->srcpad); if (GST_PAD_IS_FLUSHING (self->srcpad)) { GST_OBJECT_UNLOCK (self->srcpad); return GST_FLOW_FLUSHING; } GST_OBJECT_UNLOCK (self->srcpad); return GST_FLOW_NOT_NEGOTIATED; } } if (!self->sample) { self->sample = gst_sample_new (buffer, self->caps, &self->segment, NULL); } else { self->sample = gst_sample_make_writable (self->sample); gst_sample_set_buffer (self->sample, buffer); gst_sample_set_caps (self->sample, self->caps); gst_sample_set_segment (self->sample, &self->segment); } g_signal_emit (self, overlay_composition_signals[SIGNAL_DRAW], 0, self->sample, &compo); /* Don't store the buffer in the sample any longer, otherwise it will not * be writable below as we have one reference in the sample and one in * this function. * * If the sample is not writable itself then the application kept an * reference itself. */ if (gst_sample_is_writable (self->sample)) { gst_sample_set_buffer (self->sample, NULL); } if (!compo) { GST_DEBUG_OBJECT (self->sinkpad, "Application did not provide an overlay composition"); return gst_pad_push (self->srcpad, buffer); } /* If upstream attached a meta, we can safely add our own things * in it. Upstream must've checked that downstream supports it */ upstream_compo_meta = gst_buffer_get_video_overlay_composition_meta (buffer); if (upstream_compo_meta) { GstVideoOverlayComposition *merged_compo = gst_video_overlay_composition_copy (upstream_compo_meta->overlay); guint i, n; GST_DEBUG_OBJECT (self->sinkpad, "Appending to upstream overlay composition"); n = gst_video_overlay_composition_n_rectangles (compo); for (i = 0; i < n; i++) { GstVideoOverlayRectangle *rect = gst_video_overlay_composition_get_rectangle (compo, i); gst_video_overlay_composition_add_rectangle (merged_compo, rect); } gst_video_overlay_composition_unref (compo); gst_video_overlay_composition_unref (upstream_compo_meta->overlay); upstream_compo_meta->overlay = merged_compo; } else if (self->attach_compo_to_buffer) { GST_DEBUG_OBJECT (self->sinkpad, "Attaching as meta"); buffer = gst_buffer_make_writable (buffer); gst_buffer_add_video_overlay_composition_meta (buffer, compo); gst_video_overlay_composition_unref (compo); } else { GstVideoFrame frame; buffer = gst_buffer_make_writable (buffer); if (!gst_video_frame_map (&frame, &self->info, buffer, GST_MAP_READWRITE)) { gst_video_overlay_composition_unref (compo); goto map_failed; } gst_video_overlay_composition_blend (compo, &frame); gst_video_frame_unmap (&frame); gst_video_overlay_composition_unref (compo); } return gst_pad_push (self->srcpad, buffer); map_failed: { GST_ERROR_OBJECT (self->sinkpad, "Failed to map buffer"); gst_buffer_unref (buffer); return GST_FLOW_ERROR; } }