This commit is contained in:
2025-05-03 19:54:30 +08:00
parent a962612d84
commit a573842050
615 changed files with 93 additions and 73 deletions

View File

@ -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");
}

View File

@ -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);