webrtc_sink_dump: Add support for incoming stream dump 47/279347/7
authorSangchul Lee <sc11.lee@samsung.com>
Sun, 7 Aug 2022 12:18:35 +0000 (21:18 +0900)
committerSangchul Lee <sc11.lee@samsung.com>
Wed, 10 Aug 2022 08:40:13 +0000 (17:40 +0900)
[Version] 0.3.199
[Issue Type] New feature

Change-Id: I106355ce8471d9ed21424329e31b0ee678045068
Signed-off-by: Sangchul Lee <sc11.lee@samsung.com>
include/webrtc_private.h
packaging/capi-media-webrtc.spec
src/webrtc_sink.c
src/webrtc_sink_dump.c [new file with mode: 0644]
src/webrtc_source_private.c

index 1162bcbaaef887201030891aaa21c428284ff31f..43b0a778e58545964f8d30b4afa4190d9cd7edbd 100644 (file)
@@ -321,7 +321,7 @@ typedef struct _webrtc_resource_s {
 } webrtc_resource_s;
 #endif
 
-typedef        struct _ini_item_general_s {
+typedef struct _ini_item_general_s {
        bool generate_dot;
        const char *dot_path;
        int stats_log_period;
@@ -580,8 +580,18 @@ typedef struct _webrtc_gst_slot_s {
        } filesrc_av[AV_IDX_MAX];
 
        webrtc_display_s *display;
+
+       struct {
+               gchar *path;
+               FILE *fp;
+       } dump;
 } webrtc_gst_slot_s;
 
+typedef struct {
+       int av_idx;
+       webrtc_gst_slot_s *slot;
+} probe_userdata_s;
+
 typedef struct _webrtc_data_channel_s {
        webrtc_s *webrtc;
        GMutex mutex;
@@ -731,6 +741,11 @@ int _get_display_mode_from_sink(webrtc_s *webrtc, unsigned int track_id, webrtc_
 int _set_display_visible_to_sink(webrtc_s *webrtc, unsigned int track_id, bool visible);
 int _get_display_visible_from_sink(webrtc_s *webrtc, unsigned int track_id, bool *visible);
 
+/* sink dump */
+GstPadProbeReturn _depayloaded_data_probe_cb(GstPad *pad, GstPadProbeInfo *info, gpointer user_data);
+void _add_probe_to_pad_for_dump(webrtc_gst_slot_s *sink, GstElement *element, void *probe_cb);
+void _remove_probe_from_pad_for_dump(webrtc_gst_slot_s *sink);
+
 /* display */
 void _video_stream_decoded_cb(GstElement *object, GstBuffer *buffer, GstPad *pad, gpointer data);
 int _apply_display(webrtc_display_s *display);
index 8ea0184c0c1e1822555fa6e495f7a974d121de9e..4c32bffaf859ff14bc5eb919da0573141ccf070d 100644 (file)
@@ -1,6 +1,6 @@
 Name:       capi-media-webrtc
 Summary:    A WebRTC library in Tizen Native API
-Version:    0.3.198
+Version:    0.3.199
 Release:    0
 Group:      Multimedia/API
 License:    Apache-2.0
index 47b9ff52bc5ab47c3e7b5eff78bbab7f7c52ffbd..7c525ac295d4785f92bb82bce8cbf21a40964372 100644 (file)
@@ -358,6 +358,17 @@ static void __invoke_track_added_cb(webrtc_s *webrtc, const gchar *name, bool is
        }
 }
 
+static bool __is_rtp_depayloader(GstElementFactory *factory)
+{
+       const gchar *klass;
+
+       RET_VAL_IF(factory == NULL, false, "factory is NULL");
+
+       klass = gst_element_factory_get_metadata(factory, GST_ELEMENT_METADATA_KLASS);
+
+       return g_strrstr(klass, "Codec/Depayloader/Network/RTP");
+}
+
 static bool __is_factory_name_for_hw(gchar *factory_name)
 {
        RET_VAL_IF(factory_name == NULL, FALSE, "factory_name is NULL");
@@ -427,9 +438,16 @@ static void __decodebin_element_added_cb(GstElement *decodebin, GstElement *elem
                                "use-inband-fec", TRUE,
                                NULL);
                }
+
        } else if (g_strrstr(factory_name, "h264parse")) {
                /* send SPS and PPS Insertion Interval every second */
                g_object_set(G_OBJECT(element), "config-interval", 1, NULL);
+               _add_probe_to_pad_for_dump(sink, element, _depayloaded_data_probe_cb);
+       }
+
+       /* all other depayloaders except h264 */
+       if (__is_rtp_depayloader(factory) && !g_strrstr(factory_name, "h264")) {
+               _add_probe_to_pad_for_dump(sink, element, _depayloaded_data_probe_cb);
        }
 
        sink->av[AV_IDX_AUDIO].render.hw_decoder_used = __is_hw_decoder_element(factory, true);
@@ -569,6 +587,8 @@ void _sink_slot_destroy_cb(gpointer data)
        if (sink->sound_stream_info.type)
                free(sink->sound_stream_info.type);
 
+       _remove_probe_from_pad_for_dump(sink);
+
        g_free(sink);
 }
 
diff --git a/src/webrtc_sink_dump.c b/src/webrtc_sink_dump.c
new file mode 100644 (file)
index 0000000..fe0ade0
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "webrtc.h"
+#include "webrtc_private.h"
+
+//LCOV_EXCL_START
+static void __dump_buffer_data_to_file(FILE *fp, GstBuffer *buffer)
+{
+       GstMapInfo map_info = GST_MAP_INFO_INIT;
+
+       RET_IF(fp == NULL, "fp is NULL");
+       RET_IF(buffer == NULL, "buffer is NULL");
+       RET_IF(gst_buffer_get_size(buffer) == 0, "empty buffer");
+
+       gst_buffer_map(buffer, &map_info, GST_MAP_READ);
+       fwrite(map_info.data, map_info.size, 1, fp);
+       gst_buffer_unmap(buffer, &map_info);
+}
+
+GstPadProbeReturn _depayloaded_data_probe_cb(GstPad *pad, GstPadProbeInfo *info, gpointer user_data)
+{
+       probe_userdata_s *probe_data = (probe_userdata_s *)user_data;
+       g_autofree gchar *media_type = _get_mime_type_from_pad(pad);
+       g_auto(GStrv) str_arr = g_strsplit((const gchar *)media_type, "/", 2);
+       webrtc_gst_slot_s *sink;
+
+       RET_VAL_IF(probe_data == NULL, GST_PAD_PROBE_REMOVE, "probe_data is NULL");
+
+       sink = probe_data->slot;
+
+       if (!sink->webrtc->ini.general.dump_incoming_streams)
+               return GST_PAD_PROBE_OK;
+
+       if (!sink->dump.path) {
+               sink->dump.path = g_strdup_printf("%s/webrtc-%p.sink%u.%s.%s.dump",
+                       sink->webrtc->ini.general.dump_path, sink->webrtc, sink->id,
+                       probe_data->av_idx == AV_IDX_AUDIO ? "audio" : "video", str_arr[1]);
+               sink->dump.fp = fopen(sink->dump.path, "w");
+               if (!sink->dump.fp) {
+                       LOG_ERROR("failed to fopen() for %s, error:%s", sink->dump.path, g_strerror(errno));
+                       g_free(sink->dump.path);
+                       sink->dump.path = NULL;
+               } else {
+                       LOG_WARNING("dump[path:%s, fp:%p]", sink->dump.path, sink->dump.fp);
+                       __dump_buffer_data_to_file(sink->dump.fp, gst_pad_probe_info_get_buffer(info));
+               }
+       } else {
+               __dump_buffer_data_to_file(sink->dump.fp, gst_pad_probe_info_get_buffer(info));
+       }
+
+       return GST_PAD_PROBE_OK;
+}
+
+void _add_probe_to_pad_for_dump(webrtc_gst_slot_s *sink, GstElement *element, void *probe_cb)
+{
+       GstPad *pad;
+       probe_userdata_s *probe_userdata;
+       g_autofree gchar *media_type = NULL;
+       unsigned int idx;
+
+       RET_IF(sink == NULL, "sink is NULL");
+       RET_IF(element == NULL, "element is NULL");
+       RET_IF(probe_cb == NULL, "probe_cb is NULL");
+
+       if (!sink->webrtc->ini.general.dump_incoming_streams)
+               return;
+
+       pad = gst_element_get_static_pad(element, "src");
+       media_type = _get_mime_type_from_pad(pad);
+
+       if (!_is_supported_media_type(media_type)) {
+               gst_object_unref(pad);
+               return;
+       }
+
+       idx = _is_audio_media_type(media_type) ? AV_IDX_AUDIO : AV_IDX_VIDEO;
+
+       probe_userdata = g_new0(probe_userdata_s, 1);
+       probe_userdata->slot = sink;
+       probe_userdata->av_idx = idx;
+       sink->av[idx].src_pad = pad;
+       sink->av[idx].src_pad_probe_id = gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_BUFFER,
+                       probe_cb, probe_userdata, g_free);
+
+       LOG_DEBUG("sink[id:%u] for [%s] pad[%p] probe[id:%lu, callback:%p]",
+               sink->id, GET_MEDIA_TYPE_NAME(idx == AV_IDX_AUDIO), pad, sink->av[idx].src_pad_probe_id, probe_cb);
+}
+
+void _remove_probe_from_pad_for_dump(webrtc_gst_slot_s *sink)
+{
+       int idx;
+
+       RET_IF(sink == NULL, "sink is NULL");
+
+       for (idx = AV_IDX_AUDIO; idx < AV_IDX_MAX; idx++) {
+               if (sink->av[idx].src_pad_probe_id == 0)
+                       continue;
+
+               LOG_DEBUG("sink[id:%u] for [%s] pad[%p] probe_id[%lu]",
+                       sink->id, GET_MEDIA_TYPE_NAME(idx == AV_IDX_AUDIO), sink->av[idx].src_pad, sink->av[idx].src_pad_probe_id);
+
+               gst_pad_remove_probe(sink->av[idx].src_pad, sink->av[idx].src_pad_probe_id);
+               gst_object_unref(sink->av[idx].src_pad);
+
+               sink->av[idx].src_pad_probe_id = 0;
+               sink->av[idx].src_pad = NULL;
+
+               if (sink->dump.fp) {
+                       LOG_WARNING("dump[path:%s, fp:%p] is closed", sink->dump.path, sink->dump.fp);
+                       fclose(sink->dump.fp);
+                       sink->dump.fp = NULL;
+               }
+               if (sink->dump.path) {
+                       g_free(sink->dump.path);
+                       sink->dump.path = NULL;
+               }
+       }
+}
+//LCOV_EXCL_STOP
\ No newline at end of file
index 680259da809d4713e2c910ecb0e8733841484c08..ca51ec190f6825d5e2a6cc0d6e7873dae6c9a1a7 100644 (file)
 #include "webrtc_private.h"
 #include "webrtc_source_private.h"
 
-typedef struct {
-       int av_idx;
-       webrtc_gst_slot_s *source;
-} probe_userdata_s;
-
 static rtp_payload_info_s __payload_info[] = {
        { WEBRTC_TRANSCEIVER_CODEC_PCMU, MEDIA_TYPE_AUDIO_MULAW, "audio", "PCMU", 8000 },
        { WEBRTC_TRANSCEIVER_CODEC_PCMA, MEDIA_TYPE_AUDIO_ALAW, "audio", "PCMA", 8000 },
@@ -447,18 +442,18 @@ GstPadProbeReturn _payloaded_data_probe_cb(GstPad *pad, GstPadProbeInfo *info, g
 
        buffer = gst_pad_probe_info_get_buffer(info);
 
-       if (probe_data->source->av[probe_data->av_idx].pause) {
+       if (probe_data->slot->av[probe_data->av_idx].pause) {
                if (counts[probe_data->av_idx]++ % 10 == 0)
                        LOG_DEBUG("paused, drop [%s] buffer[%p] of pad[%p], source[%p], count[%u]",
                                GET_MEDIA_TYPE_NAME(probe_data->av_idx == AV_IDX_AUDIO),
-                               buffer, pad, probe_data->source, counts[probe_data->av_idx]);
+                               buffer, pad, probe_data->slot, counts[probe_data->av_idx]);
                return GST_PAD_PROBE_DROP;
        }
 
        if (counts[probe_data->av_idx] > 0) {
                counts[probe_data->av_idx] = 0;
                LOG_DEBUG("play again, [%s] buffer[%p] of pad[%p], source[%p]",
-                       GET_MEDIA_TYPE_NAME(probe_data->av_idx == AV_IDX_AUDIO), buffer, pad, probe_data->source);
+                       GET_MEDIA_TYPE_NAME(probe_data->av_idx == AV_IDX_AUDIO), buffer, pad, probe_data->slot);
        }
 
        return GST_PAD_PROBE_OK;
@@ -474,7 +469,7 @@ void _add_probe_to_pad_for_pause(webrtc_gst_slot_s *source, unsigned int idx, Gs
        RET_IF(probe_cb == NULL, "probe_cb is NULL");
 
        probe_userdata = g_new0(probe_userdata_s, 1);
-       probe_userdata->source = source;
+       probe_userdata->slot = source;
        probe_userdata->av_idx = idx;
        source->av[idx].src_pad = pad;
        source->av[idx].src_pad_probe_id = gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_BUFFER,
@@ -512,15 +507,15 @@ GstPadProbeReturn _source_data_probe_cb(GstPad *pad, GstPadProbeInfo *info, gpoi
        RET_VAL_IF(info == NULL, GST_PAD_PROBE_REMOVE, "info is NULL");
        RET_VAL_IF(info->data == NULL, GST_PAD_PROBE_REMOVE, "info->data is NULL");
        RET_VAL_IF(probe_data == NULL, GST_PAD_PROBE_REMOVE, "probe_data is NULL");
-       RET_VAL_IF(probe_data->source == NULL, GST_PAD_PROBE_REMOVE, "probe_data->source is NULL");
+       RET_VAL_IF(probe_data->slot == NULL, GST_PAD_PROBE_REMOVE, "probe_data->slot is NULL");
 
        switch (probe_data->av_idx) {
        case AV_IDX_AUDIO:
-               if (!probe_data->source->sound_stream_info.type)
+               if (!probe_data->slot->sound_stream_info.type)
                        return GST_PAD_PROBE_OK;
                break;
        case AV_IDX_VIDEO:
-               if (!probe_data->source->display)
+               if (!probe_data->slot->display)
                        return GST_PAD_PROBE_OK;
                break;
        default:
@@ -528,7 +523,7 @@ GstPadProbeReturn _source_data_probe_cb(GstPad *pad, GstPadProbeInfo *info, gpoi
                return GST_PAD_PROBE_OK;
        }
 
-       appsrc = probe_data->source->av[probe_data->av_idx].render.appsrc;
+       appsrc = probe_data->slot->av[probe_data->av_idx].render.appsrc;
        if (appsrc) {
                buffer = gst_pad_probe_info_get_buffer(info);
                LOG_VERBOSE("push buffer[%p] to the render pipeline, appsrc[%p]", buffer, appsrc);
@@ -550,7 +545,7 @@ void _add_probe_to_pad_for_render(webrtc_gst_slot_s *source, unsigned int idx, G
        RET_IF(probe_cb == NULL, "probe_cb is NULL");
 
        probe_userdata = g_new0(probe_userdata_s, 1);
-       probe_userdata->source = source;
+       probe_userdata->slot = source;
        probe_userdata->av_idx = idx;
        source->av[idx].render.src_pad = pad;
        source->av[idx].render.src_pad_probe_id = gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_BUFFER,