// Copyright 2019-2021, Collabora, Ltd. // SPDX-License-Identifier: BSL-1.0 /*! * @file * @brief An @ref xrt_frame_sink that does gst things. * @author Jakob Bornecrantz * @author Aaron Boxer * @ingroup aux_util */ #include "util/u_misc.h" #include "util/u_debug.h" #include "gstreamer/gst_internal.h" #include "gstreamer/gst_pipeline.h" /* * * Internal pipeline functions. * */ static void break_apart(struct xrt_frame_node *node) { struct gstreamer_pipeline *gp = container_of(node, struct gstreamer_pipeline, node); /* * This function is called when we are shutting down, after returning * from this function you are not allowed to call any other nodes in the * graph. But it must be safe for other nodes to call any normal * functions on us. Once the context is done calling break_aprt on all * objects it will call destroy on them. */ (void)gp; } static void destroy(struct xrt_frame_node *node) { struct gstreamer_pipeline *gp = container_of(node, struct gstreamer_pipeline, node); /* * All of the nodes has been broken apart and none of our functions will * be called, it's now safe to destroy and free ourselves. */ free(gp); } /* * * Exported functions. * */ void gstreamer_pipeline_play(struct gstreamer_pipeline *gp) { U_LOG_D("Starting pipeline"); gst_element_set_state(gp->pipeline, GST_STATE_PLAYING); } void gstreamer_pipeline_stop(struct gstreamer_pipeline *gp) { U_LOG_D("Stopping pipeline"); // Settle the pipeline. U_LOG_T("Sending EOS"); gst_element_send_event(gp->pipeline, gst_event_new_eos()); // Wait for EOS message on the pipeline bus. U_LOG_T("Waiting for EOS"); GstMessage *msg = NULL; msg = gst_bus_timed_pop_filtered(GST_ELEMENT_BUS(gp->pipeline), GST_CLOCK_TIME_NONE, GST_MESSAGE_EOS | GST_MESSAGE_ERROR); //! @todo Should check if we got an error message here or an eos. (void)msg; // Completely stop the pipeline. U_LOG_T("Setting to NULL"); gst_element_set_state(gp->pipeline, GST_STATE_NULL); } void gstreamer_pipeline_create_from_string(struct xrt_frame_context *xfctx, const char *pipeline_string, struct gstreamer_pipeline **out_gp) { gst_init(NULL, NULL); struct gstreamer_pipeline *gp = U_TYPED_CALLOC(struct gstreamer_pipeline); gp->node.break_apart = break_apart; gp->node.destroy = destroy; gp->xfctx = xfctx; // Setup pipeline. gp->pipeline = gst_parse_launch(pipeline_string, NULL); /* * Add ourselves to the context so we are destroyed. * This is done once we know everything is completed. */ xrt_frame_context_add(xfctx, &gp->node); *out_gp = gp; } void gstreamer_pipeline_create_autovideo_sink(struct xrt_frame_context *xfctx, const char *appsrc_name, struct gstreamer_pipeline **out_gp) { gst_init(NULL, NULL); struct gstreamer_pipeline *gp = U_TYPED_CALLOC(struct gstreamer_pipeline); gp->node.break_apart = break_apart; gp->node.destroy = destroy; gp->xfctx = xfctx; // Setup pipeline. gp->pipeline = gst_pipeline_new("pipeline"); GstElement *appsrc = gst_element_factory_make("appsrc", appsrc_name); GstElement *conv = gst_element_factory_make("videoconvert", "conv"); GstElement *scale = gst_element_factory_make("videoscale", "scale"); GstElement *videosink = gst_element_factory_make("autovideosink", "videosink"); gst_bin_add_many(GST_BIN(gp->pipeline), // appsrc, // conv, // scale, // videosink, // NULL); gst_element_link_many(appsrc, // conv, // scale, // videosink, // NULL); /* * Add ourselves to the context so we are destroyed. * This is done once we know everything is completed. */ xrt_frame_context_add(xfctx, &gp->node); *out_gp = gp; }