diff --git a/renderers/audio_renderer.c b/renderers/audio_renderer.c index c390e68..5075018 100644 --- a/renderers/audio_renderer.c +++ b/renderers/audio_renderer.c @@ -45,6 +45,7 @@ typedef struct audio_renderer_s { GstElement *appsrc; GstElement *pipeline; GstElement *volume; + GstBus *bus; unsigned char ct; } audio_renderer_t ; static audio_renderer_t *renderer_type[NFORMATS]; @@ -186,7 +187,7 @@ void audio_renderer_init(logger_t *render_logger, const char* audiosink, const b g_assert (renderer_type[i]->pipeline); gst_pipeline_use_clock(GST_PIPELINE_CAST(renderer_type[i]->pipeline), clock); - + renderer_type[i]->bus = gst_element_get_bus(renderer_type[i]->pipeline); renderer_type[i]->appsrc = gst_bin_get_by_name (GST_BIN (renderer_type[i]->pipeline), "audio_source"); renderer_type[i]->volume = gst_bin_get_by_name (GST_BIN (renderer_type[i]->pipeline), "volume"); switch (i) { @@ -367,6 +368,8 @@ void audio_renderer_flush() { void audio_renderer_destroy() { audio_renderer_stop(); for (int i = 0; i < NFORMATS ; i++ ) { + gst_object_unref (renderer_type[i]->bus); + renderer_type[i]->bus = NULL; gst_object_unref (renderer_type[i]->volume); renderer_type[i]->volume = NULL; gst_object_unref (renderer_type[i]->appsrc); @@ -376,3 +379,41 @@ void audio_renderer_destroy() { free(renderer_type[i]); } } + +static gboolean gstreamer_audio_pipeline_bus_callback(GstBus *bus, GstMessage *message, void *loop) { + switch (GST_MESSAGE_TYPE(message)) { + case GST_MESSAGE_ERROR: { + GError *err; + gchar *debug; + gst_message_parse_error (message, &err, &debug); + logger_log(logger, LOGGER_INFO, "GStreamer error (audio): %s %s", GST_MESSAGE_SRC_NAME(message),err->message); + g_error_free(err); + g_free(debug); + if (renderer->appsrc) { + gst_app_src_end_of_stream (GST_APP_SRC(renderer->appsrc)); + } + gst_bus_set_flushing(bus, TRUE); + gst_element_set_state (renderer->pipeline, GST_STATE_READY); + g_main_loop_quit( (GMainLoop *) loop); + break; + } + case GST_MESSAGE_EOS: + logger_log(logger, LOGGER_INFO, "GStreamer: End-Of-Stream (audio)"); + break; + case GST_MESSAGE_ELEMENT: + // many "level" messages may be sent + break; + default: + /* unhandled message */ + logger_log(logger, LOGGER_DEBUG,"GStreamer unhandled audio bus message: src = %s type = %s", + GST_MESSAGE_SRC_NAME(message), GST_MESSAGE_TYPE_NAME(message)); + break; + } + return TRUE; +} + +unsigned int audio_renderer_listen(void *loop, int id) { + g_assert(id >= 0 && id < NFORMATS); + return (unsigned int) gst_bus_add_watch(renderer_type[id]->bus,(GstBusFunc) + gstreamer_audio_pipeline_bus_callback, (gpointer) loop); +} diff --git a/renderers/audio_renderer.h b/renderers/audio_renderer.h index 232da28..d981e20 100644 --- a/renderers/audio_renderer.h +++ b/renderers/audio_renderer.h @@ -40,7 +40,7 @@ void audio_renderer_render_buffer(unsigned char* data, int *data_len, unsigned s void audio_renderer_set_volume(double volume); void audio_renderer_flush(); void audio_renderer_destroy(); - +unsigned int audio_renderer_listen(void *loop, int id); #ifdef __cplusplus } #endif diff --git a/renderers/video_renderer.c b/renderers/video_renderer.c index c701c88..7e04753 100644 --- a/renderers/video_renderer.c +++ b/renderers/video_renderer.c @@ -647,7 +647,7 @@ static void get_stream_status_name(GstStreamStatusType type, char *name, size_t } } -gboolean gstreamer_pipeline_bus_callback(GstBus *bus, GstMessage *message, void *loop) { +static gboolean gstreamer_video_pipeline_bus_callback(GstBus *bus, GstMessage *message, void *loop) { GstState old_state, new_state; const gchar no_state[] = ""; const gchar *old_state_name = no_state, *new_state_name = no_state; @@ -756,7 +756,7 @@ gboolean gstreamer_pipeline_bus_callback(GstBus *bus, GstMessage *message, void gchar *debug; gboolean flushing; gst_message_parse_error (message, &err, &debug); - logger_log(logger, LOGGER_INFO, "GStreamer error: %s %s", GST_MESSAGE_SRC_NAME(message),err->message); + logger_log(logger, LOGGER_INFO, "GStreamer error (video): %s %s", GST_MESSAGE_SRC_NAME(message),err->message); if (!hls_video && strstr(err->message,"Internal data stream error")) { logger_log(logger, LOGGER_INFO, "*** This is a generic GStreamer error that usually means that GStreamer\n" @@ -780,7 +780,7 @@ 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"); + logger_log(logger, LOGGER_INFO, "GStreamer: End-Of-Stream (video)"); if (hls_video) { gst_bus_set_flushing(bus, TRUE); gst_element_set_state (renderer_type[type]->pipeline, GST_STATE_READY); @@ -1007,5 +1007,5 @@ void video_renderer_seek(float position) { unsigned int video_renderer_listen(void *loop, int id) { g_assert(id >= 0 && id < n_renderers); return (unsigned int) gst_bus_add_watch(renderer_type[id]->bus,(GstBusFunc) - gstreamer_pipeline_bus_callback, (gpointer) loop); + gstreamer_video_pipeline_bus_callback, (gpointer) loop); } diff --git a/uxplay.cpp b/uxplay.cpp index f12e1ec..c3b41fb 100644 --- a/uxplay.cpp +++ b/uxplay.cpp @@ -164,7 +164,8 @@ static double db_high = 0.0; static bool taper_volume = false; static double initial_volume = 0.0; static bool h265_support = false; -static int n_renderers = 0; +static int n_video_renderers = 0; +static int n_audio_renderers = 0; static bool hls_support = false; static std::string url = ""; static guint gst_x11_window_id = 0; @@ -464,8 +465,10 @@ static guint g_unix_signal_add(gint signum, GSourceFunc handler, gpointer user_d #endif static void main_loop() { - guint gst_bus_watch_id[3] = { 0 }; - g_assert(n_renderers <= 3); + guint gst_video_bus_watch_id[3] = { 0 }; + g_assert(n_video_renderers <= 3); + guint gst_audio_bus_watch_id[2] = { 0 }; + g_assert(n_audio_renderers <= 2); GMainLoop *loop = g_main_loop_new(NULL,FALSE); relaunch_video = false; reset_loop = false; @@ -475,18 +478,25 @@ static void main_loop() { relaunch_video = true; if (url.empty()) { /* renderer[0] : jpeg coverart; renderer[1] h264 video; renderer[2] h265 video (optional) */ - n_renderers = h265_support ? 3 : 2; + n_video_renderers = h265_support ? 3 : 2; gst_x11_window_id = 0; } else { /* hls video will be rendered: renderer[0] : hls */ - n_renderers = 1; + n_video_renderers = 1; url.erase(); gst_x11_window_id = g_timeout_add(100, (GSourceFunc) x11_window_callback, (gpointer) loop); } - for (int i = 0; i < n_renderers; i++) { - gst_bus_watch_id[i] = (guint) video_renderer_listen((void *)loop, i); + for (int i = 0; i < n_video_renderers; i++) { + gst_video_bus_watch_id[i] = (guint) video_renderer_listen((void *)loop, i); } } + if (use_audio) { + n_audio_renderers = 2; + for (int i = 0; i < n_audio_renderers; i++) { + gst_audio_bus_watch_id[i] = (guint) audio_renderer_listen((void *)loop, i); + } + } + missed_feedback = 0; guint feedback_watch_id = g_timeout_add_seconds(1, (GSourceFunc) feedback_callback, (gpointer) loop); guint reset_watch_id = g_timeout_add(100, (GSourceFunc) reset_callback, (gpointer) loop); @@ -495,8 +505,11 @@ static void main_loop() { guint sigint_watch_id = g_unix_signal_add(SIGINT, (GSourceFunc) sigint_callback, (gpointer) loop); g_main_loop_run(loop); - for (int i = 0; i < n_renderers; i++) { - if (gst_bus_watch_id[i] > 0) g_source_remove(gst_bus_watch_id[i]); + for (int i = 0; i < n_video_renderers; i++) { + if (gst_video_bus_watch_id[i] > 0) g_source_remove(gst_video_bus_watch_id[i]); + } + for (int i = 0; i < n_audio_renderers; i++) { + if (gst_audio_bus_watch_id[i] > 0) g_source_remove(gst_audio_bus_watch_id[i]); } if (gst_x11_window_id > 0) g_source_remove(gst_x11_window_id); if (sigint_watch_id > 0) g_source_remove(sigint_watch_id);