From dabbec11d62dd9045d13ce33f2c2cd66568e9edc Mon Sep 17 00:00:00 2001 From: "backto.kim" Date: Fri, 17 Sep 2021 17:35:03 +0900 Subject: [PATCH] Change the structure of file src A separate pipeline for filesrc is added, and the existing src bin receives input with appsrc. This makes functions such as file looping convenient by separately managing pipelines. [Version] 0.2.112 [Issue Type] Improvement Change-Id: I69e1edea62515eb57987624e12bf863fa653b3fc --- include/webrtc_private.h | 6 + packaging/capi-media-webrtc.spec | 2 +- src/webrtc_private.c | 6 +- src/webrtc_source.c | 446 ++++++++++++++++++++++++------- 4 files changed, 355 insertions(+), 105 deletions(-) diff --git a/include/webrtc_private.h b/include/webrtc_private.h index 16bf224b..cfce8215 100644 --- a/include/webrtc_private.h +++ b/include/webrtc_private.h @@ -472,6 +472,10 @@ typedef struct _webrtc_gst_slot_s { gulong camerasrc_probe_id; bool video_muted; + GstElement *filesrc_pipeline; + GstBus *filesrc_bus; + guint filesrc_bus_watcher; + webrtc_display_s *display; } webrtc_gst_slot_s; @@ -650,6 +654,8 @@ int _stop_websocket(webrtc_websocket_s *ws); int _check_privilege(const char *privilege); int _check_feature(const char *feature); +int _gst_filesrc_pipeline_set_state(webrtc_s *webrtc, GstState state); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/packaging/capi-media-webrtc.spec b/packaging/capi-media-webrtc.spec index 26e03423..1e44ae41 100644 --- a/packaging/capi-media-webrtc.spec +++ b/packaging/capi-media-webrtc.spec @@ -1,6 +1,6 @@ Name: capi-media-webrtc Summary: A WebRTC library in Tizen Native API -Version: 0.2.111 +Version: 0.2.112 Release: 0 Group: Multimedia/API License: Apache-2.0 diff --git a/src/webrtc_private.c b/src/webrtc_private.c index 2b63884d..bd06ae86 100644 --- a/src/webrtc_private.c +++ b/src/webrtc_private.c @@ -1304,6 +1304,7 @@ void _gst_destroy_pipeline(webrtc_s *webrtc) int _gst_pipeline_set_state(webrtc_s *webrtc, GstState state) { GstStateChangeReturn ret; + int filesrc_ret = WEBRTC_ERROR_NONE; RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL"); RET_VAL_IF(webrtc->gst.pipeline == NULL, WEBRTC_ERROR_INVALID_OPERATION, "pipeline is NULL"); @@ -1314,6 +1315,9 @@ int _gst_pipeline_set_state(webrtc_s *webrtc, GstState state) return WEBRTC_ERROR_INVALID_OPERATION; } + filesrc_ret = _gst_filesrc_pipeline_set_state(webrtc, state); + RET_VAL_IF(filesrc_ret != WEBRTC_ERROR_NONE, filesrc_ret, "failed to change filesrc pipeline state to [%s]", gst_element_state_get_name(state)); + return WEBRTC_ERROR_NONE; } @@ -1748,4 +1752,4 @@ int _apply_stream_info(GstElement *element, const char *stream_type, int stream_ gst_structure_free(structure); return WEBRTC_ERROR_NONE; -} \ No newline at end of file +} diff --git a/src/webrtc_source.c b/src/webrtc_source.c index b9a4efac..d78ec380 100644 --- a/src/webrtc_source.c +++ b/src/webrtc_source.c @@ -55,7 +55,10 @@ #define DEFAULT_NAME_VIDEO_PAYLOAD "videoPayload" #define DEFAULT_NAME_VIDEOCROP "videoCrop" #define DEFAULT_NAME_SCREENSRC "waylandSrc" - +#define DEFAULT_NAME_AUDIO_FAKESINK "audioFakeSink" +#define DEFAULT_NAME_VIDEO_FAKESINK "videoFakeSink" +#define DEFAULT_NAME_AUDIO_APPSRC "audioAppsrc" +#define DEFAULT_NAME_VIDEO_APPSRC "videoAppsrc" #define APPEND_ELEMENT(x_list, x_element) \ do { \ @@ -1643,14 +1646,14 @@ exit: return ret; } -static int __build_rest_of_filesrc_elements(webrtc_gst_slot_s *source, media_type_e media_type) +static int __build_filesrc_bin(webrtc_gst_slot_s *source, media_type_e media_type) { int ret = WEBRTC_ERROR_NONE; gboolean is_audio; GstPad *src_pad = NULL; + GstElement *appsrc = NULL; GstElement *queue = NULL; GstElement *capsfilter = NULL; - unsigned int payload_id; RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL"); RET_VAL_IF(source->bin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "bin is NULL"); @@ -1662,51 +1665,58 @@ static int __build_rest_of_filesrc_elements(webrtc_gst_slot_s *source, media_typ ret = _add_no_target_ghostpad_to_slot(source, true, &src_pad); RET_VAL_IF(ret != WEBRTC_ERROR_NONE, ret, "failed to _add_no_target_ghostpad_to_slot()"); - if (!(queue = _create_element("queue", is_audio ? DEFAULT_NAME_AUDIO_QUEUE : DEFAULT_NAME_VIDEO_QUEUE))) + if (!(appsrc = _create_element("appsrc", is_audio ? DEFAULT_NAME_AUDIO_APPSRC : DEFAULT_NAME_VIDEO_APPSRC))) + return WEBRTC_ERROR_INVALID_OPERATION; + + g_object_set(G_OBJECT(appsrc), + "is-live", TRUE, + "format", GST_FORMAT_TIME, + NULL); + + if (!(queue = _create_element("queue", is_audio ? DEFAULT_NAME_AUDIO_QUEUE : DEFAULT_NAME_VIDEO_QUEUE))) { + SAFE_GST_OBJECT_UNREF(appsrc); return WEBRTC_ERROR_INVALID_OPERATION; + } if (!(capsfilter = _create_element("capsfilter", is_audio ? DEFAULT_NAME_AUDIO_CAPSFILTER : DEFAULT_NAME_VIDEO_CAPSFILTER))) { + SAFE_GST_OBJECT_UNREF(appsrc); SAFE_GST_OBJECT_UNREF(queue); return WEBRTC_ERROR_INVALID_OPERATION; } - gst_bin_add_many(source->bin, queue, capsfilter, NULL); + gst_bin_add_many(source->bin, appsrc, queue, capsfilter, NULL); + + if (!gst_element_link_many(appsrc, queue, capsfilter, NULL)) { + LOG_ERROR("failed to gst_element_link_many()"); + goto error; + } + + if (!gst_element_sync_state_with_parent(appsrc)) { + LOG_ERROR("failed to gst_element_sync_state_with_parent() for [%s]", GST_ELEMENT_NAME(appsrc)); + goto error; + } if (!gst_element_sync_state_with_parent(queue)) { LOG_ERROR("failed to gst_element_sync_state_with_parent() for [%s]", GST_ELEMENT_NAME(queue)); - gst_bin_remove_many(source->bin, queue, capsfilter, NULL); - return WEBRTC_ERROR_INVALID_OPERATION; + goto error; } if (!gst_element_sync_state_with_parent(capsfilter)) { LOG_ERROR("failed to gst_element_sync_state_with_parent() for [%s]", GST_ELEMENT_NAME(capsfilter)); - gst_bin_remove_many(source->bin, queue, capsfilter, NULL); - return WEBRTC_ERROR_INVALID_OPERATION; - } - - if (!gst_element_link_many(queue, capsfilter, NULL)) { - LOG_ERROR("failed to gst_element_link_many()"); - gst_bin_remove_many(source->bin, queue, capsfilter, NULL); - return WEBRTC_ERROR_INVALID_OPERATION; + goto error; } ret = _set_ghost_pad_target(src_pad, capsfilter, true); - if (ret != WEBRTC_ERROR_NONE) { - gst_bin_remove_many(source->bin, queue, capsfilter, NULL); - return ret; - } + if (ret != WEBRTC_ERROR_NONE) + goto error; __add_probe_to_pad_for_pause(source, is_audio ? AV_IDX_AUDIO : AV_IDX_VIDEO, src_pad, __payloaded_data_probe_cb); - payload_id = __get_available_payload_id(source->webrtc); - if (payload_id == 0) { - gst_bin_remove_many(source->bin, queue, capsfilter, NULL); - return WEBRTC_ERROR_INVALID_OPERATION; - } - - source->av[is_audio ? AV_IDX_AUDIO : AV_IDX_VIDEO].payload_id = payload_id; - return WEBRTC_ERROR_NONE; + +error: + gst_bin_remove_many(source->bin, appsrc, queue, capsfilter, NULL); + return WEBRTC_ERROR_INVALID_OPERATION; } static void __remove_rest_of_filesrc_element(webrtc_gst_slot_s *source, bool is_audio) @@ -1723,14 +1733,130 @@ static void __remove_rest_of_filesrc_element(webrtc_gst_slot_s *source, bool is_ gst_bin_remove_many(source->bin, queue, capsfilter, NULL); } +static void __filesrc_pipeline_audio_stream_handoff_cb(GstElement *object, GstBuffer *buffer, GstPad *pad, gpointer data) +{ + webrtc_gst_slot_s *source = data; + GstFlowReturn gst_ret = GST_FLOW_OK; + + g_signal_emit_by_name(gst_bin_get_by_name(source->bin, DEFAULT_NAME_AUDIO_APPSRC), "push-buffer", buffer, &gst_ret, NULL); + if (gst_ret != GST_FLOW_OK) + LOG_ERROR("failed to 'push-buffer', gst_ret[0x%x]", gst_ret); +} + +static void __filesrc_pipeline_video_stream_handoff_cb(GstElement *object, GstBuffer *buffer, GstPad *pad, gpointer data) +{ + webrtc_gst_slot_s *source = data; + GstFlowReturn gst_ret = GST_FLOW_OK; + + g_signal_emit_by_name(gst_bin_get_by_name(source->bin, DEFAULT_NAME_VIDEO_APPSRC), "push-buffer", buffer, &gst_ret, NULL); + if (gst_ret != GST_FLOW_OK) + LOG_ERROR("failed to 'push-buffer', gst_ret[0x%x]", gst_ret); +} + +static GstPadProbeReturn __fakesink_probe_cb(GstPad *pad, GstPadProbeInfo *info, gpointer u_data) +{ + webrtc_gst_slot_s *source = u_data; + GstCaps *new_cap = NULL; + GstElement *appsrc = NULL; + gchar *media = NULL; + + gst_structure_get(gst_caps_get_structure(gst_pad_get_current_caps(pad), 0), "media", G_TYPE_STRING, &media, NULL); + + if (g_strrstr(media, "audio")) + appsrc = gst_bin_get_by_name(source->bin, DEFAULT_NAME_AUDIO_APPSRC); + else + appsrc = gst_bin_get_by_name(source->bin, DEFAULT_NAME_VIDEO_APPSRC); + + RET_VAL_IF(appsrc == NULL, GST_PAD_PROBE_OK, "There is no appsrc for [%s]", media); + + new_cap = gst_caps_copy(gst_pad_get_current_caps(pad)); + g_object_set(G_OBJECT(appsrc), "caps", new_cap, NULL); + + LOG_INFO("setting caps for [%s appsrc] successfully", media); + PRINT_CAPS(new_cap, "appsrc"); + + return GST_PAD_PROBE_REMOVE; +} + +static int __create_rest_of_elements_for_filesrc_pipeline(webrtc_gst_slot_s *source, GstElement *payloader, bool is_audio) +{ + GstBin *bin = NULL; + GstElement *capsfilter = NULL; + GstElement *fakesink = NULL; + GstCaps *sink_caps = NULL; + unsigned int payload_id; + GstPad *sink_pad = NULL; + + RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL"); + RET_VAL_IF(payloader == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "payloader is NULL"); + RET_VAL_IF(source->filesrc_pipeline == NULL, WEBRTC_ERROR_INVALID_OPERATION, "filesrc_pipeline is NULL"); + + bin = GST_BIN(source->filesrc_pipeline); + + if (!(capsfilter = _create_element(DEFAULT_ELEMENT_CAPSFILTER, is_audio ? DEFAULT_NAME_AUDIO_CAPSFILTER : DEFAULT_NAME_VIDEO_CAPSFILTER))) + return WEBRTC_ERROR_INVALID_OPERATION; + + payload_id = __get_available_payload_id(source->webrtc); + if (payload_id == 0) { + SAFE_GST_OBJECT_UNREF(capsfilter); + return WEBRTC_ERROR_INVALID_OPERATION; + } + + source->av[is_audio ? AV_IDX_AUDIO : AV_IDX_VIDEO].payload_id = payload_id; + + if ((sink_caps = __make_rtp_caps(is_audio ? "audio" : "video", payload_id))) { + g_object_set(G_OBJECT(capsfilter), "caps", sink_caps, NULL); + gst_caps_unref(sink_caps); + } + + if (!(fakesink = _create_element("fakesink", is_audio ? DEFAULT_NAME_AUDIO_FAKESINK : DEFAULT_NAME_VIDEO_FAKESINK))) { + SAFE_GST_OBJECT_UNREF(capsfilter); + return WEBRTC_ERROR_INVALID_OPERATION; + } + + sink_pad = gst_element_get_static_pad(fakesink, "sink"); + gst_pad_add_probe(sink_pad, GST_PAD_PROBE_TYPE_BUFFER, __fakesink_probe_cb, source, NULL); + gst_object_unref(sink_pad); + + g_object_set(G_OBJECT(fakesink), "sync", true, NULL); + g_object_set(fakesink, "signal-handoffs", TRUE, NULL); + g_signal_connect(fakesink, "handoff", is_audio ? G_CALLBACK(__filesrc_pipeline_audio_stream_handoff_cb) : G_CALLBACK(__filesrc_pipeline_video_stream_handoff_cb), (gpointer)source); + + gst_bin_add_many(GST_BIN(source->filesrc_pipeline), capsfilter, fakesink, NULL); + + if (!gst_element_link_many(payloader, capsfilter, fakesink, NULL)) { + LOG_ERROR("failed to gst_element_link_many()"); + gst_bin_remove_many(bin, capsfilter, fakesink, NULL); + return WEBRTC_ERROR_INVALID_OPERATION; + } + + if (!gst_element_sync_state_with_parent(capsfilter)) { + LOG_ERROR("failed to gst_element_sync_state_with_parent() for [%s]", GST_ELEMENT_NAME(capsfilter)); + gst_bin_remove_many(bin, capsfilter, fakesink, NULL); + return WEBRTC_ERROR_INVALID_OPERATION; + } + + if (!gst_element_sync_state_with_parent(fakesink)) { + LOG_ERROR("failed to gst_element_sync_state_with_parent() for [%s]", GST_ELEMENT_NAME(fakesink)); + gst_bin_remove_many(bin, capsfilter, fakesink, NULL); + return WEBRTC_ERROR_INVALID_OPERATION; + } + + return WEBRTC_ERROR_NONE; +} + static GstElement * __link_decodebin_with_payload(GstPad *pad, webrtc_gst_slot_s *source, bool is_audio, bool create) { element_info_s elem_info; + GstBin *bin = NULL; GstElement *payload = NULL; GstPad *payload_sink_pad = NULL; RET_VAL_IF(pad == NULL, NULL, "pad is NULL"); RET_VAL_IF(source == NULL, NULL, "source is NULL"); + RET_VAL_IF(source->filesrc_pipeline == NULL, NULL, "filesrc_pipeline is NULL"); + + bin = GST_BIN(source->filesrc_pipeline); if (create) { CREATE_ELEMENT_FROM_REGISTRY(elem_info, GST_KLASS_NAME_PAYLOADER_RTP, @@ -1742,86 +1868,53 @@ static GstElement * __link_decodebin_with_payload(GstPad *pad, webrtc_gst_slot_s gst_element_set_name(payload, is_audio ? DEFAULT_NAME_AUDIO_PAYLOAD : DEFAULT_NAME_VIDEO_PAYLOAD); - gst_bin_add(source->bin, payload); + gst_bin_add(bin, payload); if (!gst_element_sync_state_with_parent(payload)) { LOG_ERROR("failed to gst_element_sync_state_with_parent() for [%s]", GST_ELEMENT_NAME(payload)); - gst_bin_remove(source->bin, payload); + gst_bin_remove(bin, payload); return NULL; } } else { - payload = gst_bin_get_by_name(source->bin, is_audio ? DEFAULT_NAME_AUDIO_PAYLOAD : DEFAULT_NAME_VIDEO_PAYLOAD); + payload = gst_bin_get_by_name(bin, is_audio ? DEFAULT_NAME_AUDIO_PAYLOAD : DEFAULT_NAME_VIDEO_PAYLOAD); RET_VAL_IF(payload == NULL, NULL, "There is no payload to link"); } payload_sink_pad = gst_element_get_static_pad(payload, "sink"); if (!payload_sink_pad) { LOG_ERROR("payload_sink_pad is NULL"); - gst_bin_remove(source->bin, payload); + gst_bin_remove(bin, payload); return NULL; } if (gst_pad_link(pad, payload_sink_pad) != GST_PAD_LINK_OK) { LOG_ERROR("failed to gst_pad_link()"); - gst_bin_remove(source->bin, payload); + gst_bin_remove(bin, payload); g_object_unref(payload_sink_pad); return NULL; } g_object_unref(payload_sink_pad); - LOG_INFO("decidebin is linked to [%s]", GST_ELEMENT_NAME(payload)); + LOG_INFO("decodebin is linked to [%s]", GST_ELEMENT_NAME(payload)); return payload; } -static int __link_payload_with_rest_of_filesrc_elements(webrtc_gst_slot_s *source, const gchar *media_type, GstElement *payload) -{ - gboolean is_audio; - GstElement *queue = NULL; - GstElement *capsfilter = NULL; - unsigned int payload_id = 0; - GstCaps *sink_caps = NULL; - - RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL"); - RET_VAL_IF(media_type == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "media_type is NULL"); - RET_VAL_IF(payload == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "payload is NULL"); - - is_audio = (g_strrstr(media_type, "audio")) ? TRUE : FALSE; - - queue = gst_bin_get_by_name(source->bin, is_audio ? DEFAULT_NAME_AUDIO_QUEUE : DEFAULT_NAME_VIDEO_QUEUE); - RET_VAL_IF(queue == NULL, WEBRTC_ERROR_INVALID_OPERATION, "queue is NULL"); - - capsfilter = gst_bin_get_by_name(source->bin, is_audio ? DEFAULT_NAME_AUDIO_CAPSFILTER : DEFAULT_NAME_VIDEO_CAPSFILTER); - RET_VAL_IF(capsfilter == NULL, WEBRTC_ERROR_INVALID_OPERATION, "capsfilter is NULL"); - - payload_id = source->av[is_audio ? AV_IDX_AUDIO : AV_IDX_VIDEO].payload_id; - - if (!gst_element_link(payload, queue)) { - LOG_ERROR("failed to gst_element_link() [%s] - [%s]", GST_ELEMENT_NAME(payload), GST_ELEMENT_NAME(queue)); - return WEBRTC_ERROR_INVALID_OPERATION; - } - - if ((sink_caps = __make_rtp_caps(media_type, payload_id))) { - g_object_set(G_OBJECT(capsfilter), "caps", sink_caps, NULL); - gst_caps_unref(sink_caps); - } - - LOG_INFO("[%s] is linked to [%s]", GST_ELEMENT_NAME(payload), GST_ELEMENT_NAME(queue)); - - return WEBRTC_ERROR_NONE; -} - -static void __filesrc_decodebin_pad_added_cb(GstElement *element, GstPad *pad, gpointer data) +static void __filesrc_pipeline_decodebin_pad_added_cb(GstElement *element, GstPad *pad, gpointer data) { int ret = WEBRTC_ERROR_NONE; webrtc_gst_slot_s *source = data; + GstBin *bin = NULL; const gchar *media_type = NULL; GstElement *payload = NULL; gboolean is_audio; int av_idx; RET_IF(source == NULL, "source is NULL"); + RET_IF(source->filesrc_pipeline == NULL, "filesrc_pipeline is NULL"); + + bin = GST_BIN(source->filesrc_pipeline); media_type = gst_structure_get_name(gst_caps_get_structure(gst_pad_get_current_caps(pad), 0)); RET_IF(media_type == NULL, "media_type is NULL"); @@ -1845,25 +1938,24 @@ static void __filesrc_decodebin_pad_added_cb(GstElement *element, GstPad *pad, g payload = __link_decodebin_with_payload(pad, source, is_audio, true); RET_IF(payload == NULL, "failed to __link_decodebin_with_payload()"); - ret = __build_rest_of_filesrc_elements(source, is_audio ? MEDIA_TYPE_AUDIO : MEDIA_TYPE_VIDEO); + ret = __create_rest_of_elements_for_filesrc_pipeline(source, payload, is_audio); if (ret != WEBRTC_ERROR_NONE) { - LOG_ERROR("failed to __build_rest_of_filesrc_elements()"); - gst_bin_remove(source->bin, payload); + LOG_ERROR("failed to __create_rest_of_elements_for_filesrc_pipeline()"); + gst_bin_remove(bin, payload); return; } - ret = __link_payload_with_rest_of_filesrc_elements(source, media_type, payload); + ret = __build_filesrc_bin(source, is_audio ? MEDIA_TYPE_AUDIO : MEDIA_TYPE_VIDEO); if (ret != WEBRTC_ERROR_NONE) { - LOG_ERROR("failed to __link_payload_with_rest_of_filesrc_elements()"); - gst_bin_remove(source->bin, payload); - __remove_rest_of_filesrc_element(source, is_audio); + LOG_ERROR("failed to __build_filesrc_bin()"); + gst_bin_remove(bin, payload); return; } ret = __link_source_with_webrtcbin(source, source->webrtc->gst.webrtcbin); if (ret != WEBRTC_ERROR_NONE) { LOG_ERROR("failed to __link_source_with_webrtcbin()"); - gst_bin_remove(source->bin, payload); + gst_bin_remove(bin, payload); __remove_rest_of_filesrc_element(source, is_audio); g_hash_table_remove(source->webrtc->gst.source_slots, GST_ELEMENT_NAME(source->bin)); return; @@ -1874,7 +1966,7 @@ static void __filesrc_decodebin_pad_added_cb(GstElement *element, GstPad *pad, g __add_probe_to_pad_for_render(source, av_idx, pad, __source_data_probe_cb); } -static GstAutoplugSelectResult __filesrc_decodebin_autoplug_select_cb(GstElement *bin, GstPad *pad, GstCaps *caps, GstElementFactory* factory, gpointer udata) +static GstAutoplugSelectResult __filesrc_pipeline_decodebin_autoplug_select_cb(GstElement *bin, GstPad *pad, GstCaps *caps, GstElementFactory* factory, gpointer udata) { const gchar *klass = gst_element_factory_get_metadata(factory, GST_ELEMENT_METADATA_KLASS); @@ -1886,7 +1978,7 @@ static GstAutoplugSelectResult __filesrc_decodebin_autoplug_select_cb(GstElement return GST_AUTOPLUG_SELECT_TRY; } -static void __filesrc_decodebin_pad_removed_cb(GstElement *element, GstPad *pad, gpointer data) +static void __filesrc_pipeline_decodebin_pad_removed_cb(GstElement *element, GstPad *pad, gpointer data) { webrtc_gst_slot_s *source = data; int idx = 0; @@ -1909,7 +2001,91 @@ static void __filesrc_decodebin_pad_removed_cb(GstElement *element, GstPad *pad, } } -static int __build_filesrc(webrtc_s *webrtc, webrtc_gst_slot_s *source) +static gboolean __filesrc_pipeline_bus_watch_cb(GstBus *bus, GstMessage *message, gpointer user_data) +{ + webrtc_gst_slot_s *source = (webrtc_gst_slot_s *)user_data; + GError *err = NULL; + GstState gst_state_old = GST_STATE_VOID_PENDING; + GstState gst_state_new = GST_STATE_VOID_PENDING; + GstState gst_state_pending = GST_STATE_VOID_PENDING; + + RET_VAL_IF(source == NULL, FALSE, "source is NULL"); + RET_VAL_IF(source->filesrc_pipeline == NULL, FALSE, "pipeline is NULL"); + + if (message == NULL) { + LOG_DEBUG("message is null"); + return TRUE; + } + + switch (GST_MESSAGE_TYPE(message)) { + case GST_MESSAGE_ERROR: + gst_message_parse_error(message, &err, NULL); + + LOG_ERROR("Error[from %s]: message[%s], code[%d]", + GST_OBJECT_NAME(GST_OBJECT_CAST(GST_ELEMENT(GST_MESSAGE_SRC(message)))), err->message, err->code); + + g_error_free(err); + break; + + case GST_MESSAGE_STATE_CHANGED: + if (GST_MESSAGE_SRC(message) != GST_OBJECT(source->filesrc_pipeline)) + return TRUE; + + gst_message_parse_state_changed(message, &gst_state_old, &gst_state_new, &gst_state_pending); + + LOG_INFO("GST_MESSAGE_STATE_CHANGED: Old[GST_STATE_%s] New[GST_STATE_%s] Pending[GST_STATE_%s]", + gst_element_state_get_name(gst_state_old), gst_element_state_get_name(gst_state_new), + gst_element_state_get_name(gst_state_pending)); + break; + + case GST_MESSAGE_ASYNC_DONE: + if (GST_MESSAGE_SRC(message) != GST_OBJECT(source->filesrc_pipeline)) + return TRUE; + + LOG_INFO("GST_MESSAGE_ASYNC_DONE"); + break; + + case GST_MESSAGE_EOS: + LOG_INFO("GST_MESSAGE_EOS end-of-stream"); + +#if 0 //ToDo + gst_element_seek(source->filesrc_pipeline, + 1.0, + GST_FORMAT_TIME, + GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, + GST_SEEK_TYPE_SET, 0, + GST_SEEK_TYPE_NONE, 0); +#endif + break; + + default: + break; + } + + return TRUE; +} + +static void __destroy_filesrc_pipeline(webrtc_gst_slot_s *source) +{ + RET_IF(source == NULL, "source is NULL"); + + if (source->filesrc_bus_watcher > 0) { + gst_bus_remove_watch(source->filesrc_bus); + source->filesrc_bus_watcher = 0; + } + + if (source->filesrc_bus) { + gst_object_unref(source->filesrc_bus); + source->filesrc_bus = NULL; + } + + if (source->filesrc_pipeline) { + gst_object_unref(source->filesrc_pipeline); + source->filesrc_pipeline = NULL; + } +} + +static int __build_filesrc_pipeline(webrtc_s *webrtc, webrtc_gst_slot_s *source) { GstElement *filesrc = NULL; GstElement *decodebin = NULL; @@ -1918,27 +2094,45 @@ static int __build_filesrc(webrtc_s *webrtc, webrtc_gst_slot_s *source) RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL"); RET_VAL_IF(source->bin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "bin is NULL"); + source->filesrc_pipeline = gst_pipeline_new("filesrc-pipeline"); + RET_VAL_IF(source->filesrc_pipeline == NULL, WEBRTC_ERROR_INVALID_OPERATION, "pipeline is NULL"); + + if (!(source->filesrc_bus = gst_pipeline_get_bus(GST_PIPELINE(source->filesrc_pipeline)))) { + LOG_ERROR("failed to gst_pipeline_get_bus()"); + goto error; + } + + if ((source->filesrc_bus_watcher = gst_bus_add_watch(source->filesrc_bus, (GstBusFunc)__filesrc_pipeline_bus_watch_cb, source)) == 0) { + LOG_ERROR("failed to gst_bus_add_watch()"); + goto error; + } + if (!(filesrc = _create_element("filesrc", DEFAULT_NAME_FILE_SRC))) - return WEBRTC_ERROR_INVALID_OPERATION; + goto error; if (!(decodebin = _create_element("decodebin", NULL))) { SAFE_GST_OBJECT_UNREF(filesrc); - return WEBRTC_ERROR_INVALID_OPERATION; + goto error; } - gst_bin_add_many(source->bin, filesrc, decodebin, NULL); + gst_bin_add_many(GST_BIN(source->filesrc_pipeline), filesrc, decodebin, NULL); if (!gst_element_link(filesrc, decodebin)) { + gst_bin_remove_many(GST_BIN(source->filesrc_pipeline), filesrc, decodebin, NULL); LOG_ERROR("failed to gst_element_link()"); - gst_bin_remove_many(source->bin, filesrc, decodebin, NULL); - return WEBRTC_ERROR_INVALID_OPERATION; + goto error; } - g_signal_connect(decodebin, "autoplug-select", G_CALLBACK(__filesrc_decodebin_autoplug_select_cb), NULL); - g_signal_connect(decodebin, "pad-added", G_CALLBACK(__filesrc_decodebin_pad_added_cb), (gpointer)source); - g_signal_connect(decodebin, "pad-removed", G_CALLBACK(__filesrc_decodebin_pad_removed_cb), (gpointer)source); + g_signal_connect(decodebin, "autoplug-select", G_CALLBACK(__filesrc_pipeline_decodebin_autoplug_select_cb), NULL); + g_signal_connect(decodebin, "pad-added", G_CALLBACK(__filesrc_pipeline_decodebin_pad_added_cb), (gpointer)source); + g_signal_connect(decodebin, "pad-removed", G_CALLBACK(__filesrc_pipeline_decodebin_pad_removed_cb), (gpointer)source); return WEBRTC_ERROR_NONE; + +error: + __destroy_filesrc_pipeline(source); + return WEBRTC_ERROR_INVALID_OPERATION; + } static void _appsrc_need_data_cb(GstElement *appsrc, guint size, gpointer data) @@ -2135,7 +2329,7 @@ static int __build_source_bin(webrtc_s *webrtc, webrtc_gst_slot_s *source) return __build_screensrc(webrtc, source); case WEBRTC_MEDIA_SOURCE_TYPE_FILE: - return __build_filesrc(webrtc, source); + return __build_filesrc_pipeline(webrtc, source); case WEBRTC_MEDIA_SOURCE_TYPE_MEDIA_PACKET: return __build_mediapacketsrc(webrtc, source); @@ -2238,6 +2432,9 @@ void _source_slot_destroy_cb(gpointer data) if (source->sound_stream_info.type) free(source->sound_stream_info.type); + if (source->type == WEBRTC_MEDIA_SOURCE_TYPE_FILE) + __destroy_filesrc_pipeline(source); + g_free(source); } @@ -2778,28 +2975,42 @@ bool _check_if_format_is_set_to_packet_sources(webrtc_s *webrtc) static void __remove_filesrc_element(webrtc_gst_slot_s *source) { + GstBin *bin = NULL; GstElement *payload = NULL; - GstElement *queue = NULL; GstElement *capsfilter = NULL; + GstElement *fakesink = NULL; - payload = gst_bin_get_by_name(source->bin, DEFAULT_NAME_AUDIO_PAYLOAD); + RET_IF(source == NULL, "source is NULL"); + RET_IF(source->filesrc_pipeline == NULL, "filesrc_pipeline is NULL"); + + bin = GST_BIN(source->filesrc_pipeline); + + payload = gst_bin_get_by_name(bin, DEFAULT_NAME_AUDIO_PAYLOAD); if (payload) { - queue = gst_bin_get_by_name(source->bin, DEFAULT_NAME_AUDIO_QUEUE); - capsfilter = gst_bin_get_by_name(source->bin, DEFAULT_NAME_AUDIO_CAPSFILTER); - gst_bin_remove_many(source->bin, payload, queue, capsfilter, NULL); + capsfilter = gst_bin_get_by_name(bin, DEFAULT_NAME_AUDIO_CAPSFILTER); + fakesink = gst_bin_get_by_name(bin, DEFAULT_NAME_AUDIO_FAKESINK); + gst_bin_remove_many(bin, payload, capsfilter, fakesink, NULL); + + if (source->av[AV_IDX_AUDIO].payload_id > 0) + __return_payload_id(source->webrtc, source->av[AV_IDX_AUDIO].payload_id); __remove_probe_from_pad_for_pause(source, AV_IDX_AUDIO); } - payload = gst_bin_get_by_name(source->bin, DEFAULT_NAME_VIDEO_PAYLOAD); + payload = gst_bin_get_by_name(bin, DEFAULT_NAME_VIDEO_PAYLOAD); if (payload) { - queue = gst_bin_get_by_name(source->bin, DEFAULT_NAME_VIDEO_QUEUE); - capsfilter = gst_bin_get_by_name(source->bin, DEFAULT_NAME_VIDEO_CAPSFILTER); - gst_bin_remove_many(source->bin, payload, queue, capsfilter, NULL); + capsfilter = gst_bin_get_by_name(bin, DEFAULT_NAME_VIDEO_CAPSFILTER); + fakesink = gst_bin_get_by_name(bin, DEFAULT_NAME_VIDEO_FAKESINK); + gst_bin_remove_many(bin, payload, capsfilter, fakesink, NULL); + + if (source->av[AV_IDX_VIDEO].payload_id > 0) + __return_payload_id(source->webrtc, source->av[AV_IDX_VIDEO].payload_id); __remove_probe_from_pad_for_pause(source, AV_IDX_VIDEO); } + /* FIXME: filesrc_bin should be updated as well */ + source->media_types = 0; } @@ -2825,7 +3036,7 @@ int _set_media_path(webrtc_s *webrtc, unsigned int source_id, const char *path) } } - filesrc = gst_bin_get_by_name(source->bin, DEFAULT_NAME_FILE_SRC); + filesrc = gst_bin_get_by_name(GST_BIN(source->filesrc_pipeline), DEFAULT_NAME_FILE_SRC); RET_VAL_IF(filesrc == NULL, WEBRTC_ERROR_INVALID_OPERATION, "filesrc is NULL"); g_object_get(G_OBJECT(filesrc), "location", &location, NULL); @@ -2843,12 +3054,12 @@ int _set_media_path(webrtc_s *webrtc, unsigned int source_id, const char *path) static gboolean __check_path_is_not_set_cb(gpointer key, gpointer value, gpointer user_data) { - webrtc_gst_slot_s *source = value; + webrtc_gst_slot_s *source = (webrtc_gst_slot_s *)value; gchar *location = NULL; if (source->type == GPOINTER_TO_INT(user_data)) { LOG_INFO("found file source[%p, id:%u]", source, source->id); - g_object_get(G_OBJECT(gst_bin_get_by_name(source->bin, DEFAULT_NAME_FILE_SRC)), "location", &location, NULL); + g_object_get(G_OBJECT(gst_bin_get_by_name(GST_BIN(source->filesrc_pipeline), DEFAULT_NAME_FILE_SRC)), "location", &location, NULL); if (!location) return TRUE; @@ -3933,3 +4144,32 @@ int _unset_screen_source_crop(webrtc_s *webrtc, unsigned int source_id) return WEBRTC_ERROR_NONE; } + +static void __set_filesrc_pipline_state_foreach_cb(gpointer key, gpointer value, gpointer user_data) +{ + webrtc_gst_slot_s *source = (webrtc_gst_slot_s *)value; + GstStateChangeReturn ret; + GstState state = GPOINTER_TO_UINT(user_data); + + if (source->type != WEBRTC_MEDIA_SOURCE_TYPE_FILE) + return; + + LOG_INFO("found file source[%p, id:%u]", source, source->id); + + ret = gst_element_set_state(source->filesrc_pipeline, state); + if (ret == GST_STATE_CHANGE_FAILURE) { + LOG_ERROR("failed to gst_element_set_state(), state[%s]", gst_element_state_get_name(state)); + return; + } + + LOG_INFO("change pipeline state to [%s]", gst_element_state_get_name(state)); +} + +int _gst_filesrc_pipeline_set_state(webrtc_s *webrtc, GstState state) +{ + RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL"); + + g_hash_table_foreach(webrtc->gst.source_slots, __set_filesrc_pipline_state_foreach_cb, GINT_TO_POINTER(state)); + + return WEBRTC_ERROR_NONE; +} -- 2.34.1