mixer
This commit is contained in:
@ -263,38 +263,40 @@ void video_renderer_init(logger_t *render_logger, const char *server_name, video
|
||||
}
|
||||
GString *launch = g_string_new("");
|
||||
|
||||
// Create input-selector and video sink
|
||||
g_string_append(launch, "input-selector name=selector ! ");
|
||||
g_string_append(launch, videosink);
|
||||
g_string_append(launch, " name=");
|
||||
g_string_append(launch, videosink);
|
||||
g_string_append(launch, "_");
|
||||
g_string_append(launch, renderer_type[i]->codec);
|
||||
g_string_append(launch, videosink_options);
|
||||
// Create videomixer and video sink with proper format negotiation
|
||||
g_string_append(launch, "videomixer name=mixer background=black ! ");
|
||||
g_string_append(launch, "videoconvert ! ");
|
||||
g_string_append(launch, videosink);
|
||||
g_string_append(launch, " name=");
|
||||
g_string_append(launch, videosink);
|
||||
g_string_append(launch, "_");
|
||||
g_string_append(launch, renderer_type[i]->codec);
|
||||
g_string_append(launch, videosink_options);
|
||||
|
||||
if (video_sync) {
|
||||
g_string_append(launch, " sync=true");
|
||||
sync = true;
|
||||
} else {
|
||||
g_string_append(launch, " sync=false");
|
||||
sync = false;
|
||||
}
|
||||
// Add static image source branch
|
||||
g_string_append_printf(launch, " filesrc location=%s ! jpegdec ! imagefreeze ! videoconvert ! videoscale ! queue ! selector.sink_0 ", default_image);
|
||||
if (video_sync) {
|
||||
g_string_append(launch, " sync=true");
|
||||
sync = true;
|
||||
} else {
|
||||
g_string_append(launch, " sync=false");
|
||||
sync = false;
|
||||
}
|
||||
|
||||
// Add video source branch
|
||||
g_string_append(launch, " appsrc name=video_source ! ");
|
||||
g_string_append(launch, "queue ! ");
|
||||
g_string_append(launch, parser);
|
||||
g_string_append(launch, " ! ");
|
||||
g_string_append(launch, decoder);
|
||||
g_string_append(launch, " ! ");
|
||||
append_videoflip(launch, &videoflip[0], &videoflip[1]);
|
||||
g_string_append(launch, converter);
|
||||
g_string_append(launch, " ! ");
|
||||
g_string_append(launch, "videoscale ! ");
|
||||
g_string_append(launch, "queue ! ");
|
||||
g_string_append(launch, "selector.sink_1 ");
|
||||
// Add static image source branch with format negotiation
|
||||
g_string_append_printf(launch, " filesrc location=%s ! decodebin ! imagefreeze ! videoconvert ! videoscale ! queue ! mixer.sink_0 ", default_image);
|
||||
|
||||
// Add video source branch with format negotiation
|
||||
g_string_append(launch, " appsrc name=video_source ! ");
|
||||
g_string_append(launch, "queue ! ");
|
||||
g_string_append(launch, parser);
|
||||
g_string_append(launch, " ! ");
|
||||
g_string_append(launch, decoder);
|
||||
g_string_append(launch, " ! ");
|
||||
append_videoflip(launch, &videoflip[0], &videoflip[1]);
|
||||
g_string_append(launch, converter);
|
||||
g_string_append(launch, " ! ");
|
||||
g_string_append(launch, "videoscale ! ");
|
||||
g_string_append(launch, "queue ! ");
|
||||
g_string_append(launch, "mixer.sink_1 ");
|
||||
|
||||
if (!strcmp(renderer_type[i]->codec, h264)) {
|
||||
char *pos = launch->str;
|
||||
@ -324,14 +326,6 @@ void video_renderer_init(logger_t *render_logger, const char *server_name, video
|
||||
renderer_type[i]->appsrc = gst_bin_get_by_name (GST_BIN (renderer_type[i]->pipeline), "video_source");
|
||||
g_assert(renderer_type[i]->appsrc);
|
||||
|
||||
// Get the input-selector element
|
||||
GstElement *selector = gst_bin_get_by_name(GST_BIN(renderer_type[i]->pipeline), "selector");
|
||||
g_assert(selector);
|
||||
|
||||
// Set initial input to static image
|
||||
g_object_set(selector, "active-pad", gst_element_get_static_pad(selector, "sink_0"), NULL);
|
||||
gst_object_unref(selector);
|
||||
|
||||
g_object_set(renderer_type[i]->appsrc, "caps", caps, "stream-type", 0, "is-live", TRUE, "format", GST_FORMAT_TIME, NULL);
|
||||
g_string_free(launch, TRUE);
|
||||
gst_caps_unref(caps);
|
||||
@ -408,7 +402,8 @@ void video_renderer_start() {
|
||||
renderer->bus = gst_element_get_bus(renderer->pipeline);
|
||||
gst_element_set_state (renderer->pipeline, GST_STATE_PLAYING);
|
||||
return;
|
||||
}
|
||||
}
|
||||
video_renderer_switch_source(false);
|
||||
/* when not hls, start both h264 and h265 pipelines; will shut down the "wrong" one when we know the codec */
|
||||
for (int i = 0; i < n_renderers; i++) {
|
||||
gst_element_set_state (renderer_type[i]->pipeline, GST_STATE_PLAYING);
|
||||
@ -604,11 +599,11 @@ gboolean gstreamer_pipeline_bus_callback(GstBus *bus, GstMessage *message, void
|
||||
"*** to select a videosink of your choice (see \"man uxplay\").\n\n"
|
||||
"*** Raspberry Pi models 4B and earlier using Video4Linux2 may need \"-bt709\" uxplay option");
|
||||
}
|
||||
g_error_free (err);
|
||||
g_error_free (err);
|
||||
g_free (debug);
|
||||
if (renderer_type[type]->appsrc) {
|
||||
gst_app_src_end_of_stream (GST_APP_SRC(renderer_type[type]->appsrc));
|
||||
}
|
||||
if (renderer_type[type]->appsrc) {
|
||||
gst_app_src_end_of_stream (GST_APP_SRC(renderer_type[type]->appsrc));
|
||||
}
|
||||
gst_bus_set_flushing(bus, TRUE);
|
||||
gst_element_set_state (renderer_type[type]->pipeline, GST_STATE_READY);
|
||||
renderer_type[type]->terminate = TRUE;
|
||||
@ -618,10 +613,10 @@ gboolean gstreamer_pipeline_bus_callback(GstBus *bus, GstMessage *message, void
|
||||
case GST_MESSAGE_EOS:
|
||||
/* end-of-stream */
|
||||
logger_log(logger, LOGGER_INFO, "GStreamer: End-Of-Stream");
|
||||
if (hls_video) {
|
||||
if (hls_video) {
|
||||
gst_bus_set_flushing(bus, TRUE);
|
||||
gst_element_set_state (renderer_type[type]->pipeline, GST_STATE_READY);
|
||||
renderer_type[type]->terminate = TRUE;
|
||||
renderer_type[type]->terminate = TRUE;
|
||||
g_main_loop_quit( (GMainLoop *) loop);
|
||||
}
|
||||
break;
|
||||
@ -722,12 +717,18 @@ unsigned int video_reset_callback(void * loop) {
|
||||
if (video_terminate) {
|
||||
video_terminate = false;
|
||||
if (renderer->appsrc) {
|
||||
gst_app_src_end_of_stream (GST_APP_SRC(renderer->appsrc));
|
||||
gst_app_src_end_of_stream (GST_APP_SRC(renderer->appsrc));
|
||||
}
|
||||
gboolean flushing = TRUE;
|
||||
gst_bus_set_flushing(renderer->bus, flushing);
|
||||
gst_element_set_state (renderer->pipeline, GST_STATE_NULL);
|
||||
g_main_loop_quit( (GMainLoop *) loop);
|
||||
#if SWITCH_TO_IMAGE_INSTEAD_OF_QUIT
|
||||
// Instead of quitting, switch to image display
|
||||
video_renderer_switch_source(false);
|
||||
// Keep the pipeline running
|
||||
#else
|
||||
gboolean flushing = TRUE;
|
||||
gst_bus_set_flushing(renderer->bus, flushing);
|
||||
gst_element_set_state (renderer->pipeline, GST_STATE_NULL);
|
||||
g_main_loop_quit( (GMainLoop *) loop);
|
||||
#endif
|
||||
}
|
||||
return (unsigned int) TRUE;
|
||||
}
|
||||
@ -796,27 +797,36 @@ unsigned int video_renderer_listen(void *loop, int id) {
|
||||
}
|
||||
|
||||
void video_renderer_switch_source(bool use_video) {
|
||||
static bool current_use_video = false;
|
||||
if (current_use_video == use_video) {
|
||||
return;
|
||||
}
|
||||
current_use_video = use_video;
|
||||
first_packet = !use_video; //如果切换到image,则重置first_packet,使得下次收到第一个视频包时,会切换到video
|
||||
if (!renderer || hls_video) {
|
||||
return;
|
||||
}
|
||||
|
||||
GstElement *selector = gst_bin_get_by_name(GST_BIN(renderer->pipeline), "selector");
|
||||
if (!selector) {
|
||||
logger_log(logger, LOGGER_ERR, "Failed to get input-selector element");
|
||||
GstElement *mixer = gst_bin_get_by_name(GST_BIN(renderer->pipeline), "mixer");
|
||||
if (!mixer) {
|
||||
logger_log(logger, LOGGER_ERR, "Failed to get videomixer element");
|
||||
return;
|
||||
}
|
||||
|
||||
// Switch to video source (sink_1) or static image (sink_0)
|
||||
GstPad *pad = gst_element_get_static_pad(selector, use_video ? "sink_1" : "sink_0");
|
||||
if (!pad) {
|
||||
logger_log(logger, LOGGER_ERR, "Failed to get pad for %s", use_video ? "video source" : "static image");
|
||||
gst_object_unref(selector);
|
||||
return;
|
||||
// Set alpha values for both sources
|
||||
// When use_video is true, video source is fully visible (alpha=1.0) and image is transparent (alpha=0.0)
|
||||
// When use_video is false, image is fully visible (alpha=1.0) and video is transparent (alpha=0.0)
|
||||
GstPad *sink0 = gst_element_get_static_pad(mixer, "sink_0");
|
||||
GstPad *sink1 = gst_element_get_static_pad(mixer, "sink_1");
|
||||
|
||||
if (sink0 && sink1) {
|
||||
g_object_set(sink0, "alpha", use_video ? 0.0 : 1.0, NULL);
|
||||
g_object_set(sink1, "alpha", use_video ? 1.0 : 0.0, NULL);
|
||||
}
|
||||
|
||||
if (sink0) gst_object_unref(sink0);
|
||||
if (sink1) gst_object_unref(sink1);
|
||||
gst_object_unref(mixer);
|
||||
|
||||
g_object_set(selector, "active-pad", pad, NULL);
|
||||
gst_object_unref(pad);
|
||||
gst_object_unref(selector);
|
||||
|
||||
logger_log(logger, LOGGER_INFO, "Switched to %s source", use_video ? "video" : "PlaceHold image");
|
||||
logger_log(logger, LOGGER_INFO, "Switched to [%s] source", use_video ? "video" : "image");
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ typedef enum videoflip_e {
|
||||
} videoflip_t;
|
||||
|
||||
typedef struct video_renderer_s video_renderer_t;
|
||||
|
||||
#define SWITCH_TO_IMAGE_INSTEAD_OF_QUIT 1
|
||||
void video_renderer_init (logger_t *logger, const char *server_name, videoflip_t videoflip[2], const char *parser,
|
||||
const char *decoder, const char *converter, const char *videosink, const char *videosink_options,
|
||||
bool initial_fullscreen, bool video_sync, bool h265_support, const char *uri, const char *default_image);
|
||||
|
Reference in New Issue
Block a user