#include "webrtc.h"
#include "webrtc_private.h"
-#define GST_KLASS_NAME_ENCODER_AUDIO "Codec/Encoder/Audio"
-#define GST_KLASS_NAME_ENCODER_VIDEO "Codec/Encoder/Video"
-#define GST_KLASS_NAME_DECODER_AUDIO "Codec/Decoder/Audio"
-#define GST_KLASS_NAME_DECODER_VIDEO "Codec/Decoder/Video"
-#define GST_KLASS_NAME_PAYLOADER_RTP "Codec/Payloader/Network/RTP"
-#define GST_KLASS_NAME_DEPAYLOADER_RTP "Codec/Depayloader/Network/RTP"
-#define GST_KLASS_NAME_CONVERTER_AUDIO "Filter/Converter/Audio"
-#define GST_KLASS_NAME_CONVERTER_VIDEO "Filter/Converter/Video"
-
-#define DEFAULT_ELEMENT_CAMERASRC "camerasrc"
-#define DEFAULT_ELEMENT_AUDIOSRC "pulsesrc"
-#define DEFAULT_ELEMENT_VIDEOTESTSRC "videotestsrc"
-#define DEFAULT_ELEMENT_AUDIOTESTSRC "audiotestsrc"
-#define DEFAULT_ELEMENT_CAPSFILTER "capsfilter"
-#define DEFAULT_ELEMENT_QUEUE "queue"
-#define DEFAULT_ELEMENT_VIDEOCONVERT "videoconvert"
-#define DEFAULT_ELEMENT_AUDIOCONVERT "audioconvert"
-#define DEFAULT_ELEMENT_AUDIORESAMPLE "audioresample"
-#define DEFAULT_ELEMENT_VIDEOSINK "tizenwlsink"
-#define DEFAULT_ELEMENT_AUDIOSINK "pulsesink"
-
-#define DEFAULT_VIDEO_ENCODED_MEDIA_TYPE "video/x-vp8"
-#define DEFAULT_VIDEO_RAW_MEDIA_TYPE "video/x-raw"
-#define DEFAULT_VIDEO_RAW_FORMAT "I420"
-#define DEFAULT_VIDEO_WIDTH 352
-#define DEFAULT_VIDEO_HEIGHT 288
-
-#define DEFAULT_AUDIO_ENCODED_MEDIA_TYPE "audio/x-opus"
-#define DEFAULT_AUDIO_RAW_MEDIA_TYPE "audio/x-raw"
-#define DEFAULT_AUDIO_RAW_FORMAT "S16LE"
-#define DEFAULT_AUDIO_CHANNELS 1
-#define DEFAULT_AUDIO_SAMPLERATE 8000
-
#define DEFAULT_DOT_FILE_NAME_PREFIX "webrtc"
#define DEFAULT_DOT_DIRECTORY "/tmp"
-typedef enum {
- CODEC_TYPE_OPUS,
- CODEC_TYPE_VORBIS,
- CODEC_TYPE_VP8,
- CODEC_TYPE_VP9,
- CODEC_TYPE_THEORA,
- CODEC_TYPE_H263,
- CODEC_TYPE_H264,
- CODEC_TYPE_H265,
- CODEC_TYPE_NOT_SUPPORTED,
-} codec_type_e;
-
-typedef struct {
- const char *encoding_name;
- const int clock_rate;
-} payload_type_s;
-
-static payload_type_s payload_types[] = {
- /* AUDIO */
- [CODEC_TYPE_OPUS] = { "OPUS", 48000 },
- [CODEC_TYPE_VORBIS] = { "VORBIS", -1 }, /* NOTE: -1 for various clock rate */
- /* VIDEO */
- [CODEC_TYPE_VP8] = { "VP8", 90000 },
- [CODEC_TYPE_VP9] = { "VP9", 90000 },
- [CODEC_TYPE_THEORA] = { "THEORA", 90000 },
- [CODEC_TYPE_H263] = { "H263", 90000 },
- [CODEC_TYPE_H264] = { "H264", 90000 },
- [CODEC_TYPE_H265] = { "H265", 90000 },
-};
-
-#define CREATE_ELEMENT_FROM_REGISTRY(x_elem_info, x_klass_name, x_sink_caps, x_src_caps, x_element) \
-do { \
- x_elem_info.klass_name = x_klass_name; \
- x_elem_info.sink_caps = x_sink_caps; \
- x_elem_info.src_caps = x_src_caps; \
- x_element = __create_element_from_registry(&x_elem_info); \
- if (x_elem_info.sink_caps) \
- gst_caps_unref(x_elem_info.sink_caps); \
- if (x_elem_info.src_caps) \
- gst_caps_unref(x_elem_info.src_caps); \
- if (!x_element) { \
- LOG_ERROR("failed to create element of [%s]", x_klass_name); \
- return WEBRTC_ERROR_INVALID_OPERATION; \
- } \
-} while (0)
-
-#define MALLOC_AND_INIT_SLOT(x_slot, x_id, x_bin_name) \
-do { \
- x_slot = g_new0(webrtc_gst_slot_s, 1); \
- x_slot->id = x_id; \
- x_slot->bin = gst_bin_new(x_bin_name); \
- x_slot->mlines[MLINES_IDX_AUDIO] = -1; \
- x_slot->mlines[MLINES_IDX_VIDEO] = -1; \
-} while (0)
-
-#define GENERATE_DOT(x_webrtc, x_fmt, x_arg...) \
-do { \
- gchar *dot_name; \
- if (!x_webrtc->ini.generate_dot) \
- break; \
- dot_name = g_strdup_printf(""x_fmt"", x_arg); \
- __generate_dot(x_webrtc, dot_name); \
- g_free(dot_name); \
-} while (0)
-
static const char* __state_str[] = {
"IDLE",
"NEGOTIATING",
GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV
};
-static void __generate_dot(webrtc_s *webrtc, const gchar *name)
+void _generate_dot(webrtc_s *webrtc, const gchar *name)
{
gchar *dot_name;
return TRUE;
}
-static GstElement *__create_element(const char *factory_name, const char *name)
+GstElement *_create_element(const char *factory_name, const char *name)
{
GstElement *element = NULL;
return second_rank - first_rank;
}
-static GstElement *__create_element_from_registry(element_info_s *elem_info)
+GstElement *_create_element_from_registry(element_info_s *elem_info)
{
GstElement *element = NULL;
GList *factories = NULL;
if (factories) {
factory = GST_ELEMENT_FACTORY(factories->data);
LOG_DEBUG("sorted result element is [%s]", GST_OBJECT_NAME(factory));
- element = __create_element(GST_OBJECT_NAME(factory), NULL);
+ element = _create_element(GST_OBJECT_NAME(factory), NULL);
} else {
LOG_DEBUG("could not find any compatible element for klass_name[%s]", elem_info->klass_name);
}
LOG_DEBUG("[IceConnectionState] is changed to [%s]", new_state);
}
-static webrtc_gst_slot_s* __find_sink_slot(webrtc_s *webrtc, const gchar *key)
-{
- RET_VAL_IF(webrtc == NULL, NULL, "webrtc is NULL");
- RET_VAL_IF(key == NULL, NULL, "key is NULL");
-
- return g_hash_table_lookup(webrtc->gst.sink_slots, key);
-}
-
-static int __build_videosink(webrtc_s *webrtc, GstElement *decodebin, GstPad *src_pad)
-{
- webrtc_gst_slot_s *sink;
- GstElement *videoconvert;
- GstElement *videosink;
-
- RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
- RET_VAL_IF(decodebin == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "decodebin is NULL");
- RET_VAL_IF(src_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "src_pad is NULL");
-
- sink = __find_sink_slot(webrtc, GST_ELEMENT_NAME(decodebin));
- RET_VAL_IF(sink == NULL, WEBRTC_ERROR_INVALID_OPERATION, "could not find an item by [%s] in sink slots", GST_ELEMENT_NAME(decodebin));
- RET_VAL_IF(sink->bin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "bin is NULL");
-
- sink->media_types |= MEDIA_TYPE_VIDEO;
-
- if (!(videoconvert = __create_element(DEFAULT_ELEMENT_VIDEOCONVERT, NULL))) {
- LOG_ERROR("failed to create videoconvert");
- return WEBRTC_ERROR_INVALID_OPERATION;
- }
-
- /* FIXME: get factory name from ini */
- if (!(videosink = __create_element(DEFAULT_ELEMENT_VIDEOSINK, NULL))) {
- LOG_ERROR("failed to create videosink");
- return WEBRTC_ERROR_INVALID_OPERATION;
- }
-
- gst_bin_add_many(GST_BIN(sink->bin), videoconvert, videosink, NULL);
-
- if (!gst_element_sync_state_with_parent(videoconvert)) {
- LOG_ERROR("failed to gst_element_sync_state_with_parent() for [%s]", GST_ELEMENT_NAME(videoconvert));
- return WEBRTC_ERROR_INVALID_OPERATION;
- }
- if (!gst_element_sync_state_with_parent(videosink)) {
- LOG_ERROR("failed to gst_element_sync_state_with_parent() for [%s]", GST_ELEMENT_NAME(videosink));
- return WEBRTC_ERROR_INVALID_OPERATION;
- }
-
- if (!gst_element_link_many(decodebin, videoconvert, videosink, NULL)) {
- LOG_ERROR("failed to gst_element_link_many()");
- return WEBRTC_ERROR_INVALID_OPERATION;
- }
-
- return WEBRTC_ERROR_NONE;
-}
-
-static int __build_audiosink(webrtc_s *webrtc, GstElement *decodebin, GstPad *src_pad)
-{
- webrtc_gst_slot_s *sink;
- GstElement *audioconvert;
- GstElement *audioresample;
- GstElement *audiosink;
-
- RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
- RET_VAL_IF(decodebin == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "decodebin is NULL");
- RET_VAL_IF(src_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "src_pad is NULL");
-
- sink = __find_sink_slot(webrtc, GST_ELEMENT_NAME(decodebin));
- RET_VAL_IF(sink == NULL, WEBRTC_ERROR_INVALID_OPERATION, "could not find an item by [%s] in sink slots", GST_ELEMENT_NAME(decodebin));
- RET_VAL_IF(sink->bin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "bin is NULL");
-
- sink->media_types |= MEDIA_TYPE_AUDIO;
-
- if (!(audioconvert = __create_element(DEFAULT_ELEMENT_AUDIOCONVERT, NULL))) {
- LOG_ERROR("failed to create audioconvert");
- return WEBRTC_ERROR_INVALID_OPERATION;
- }
-
- if (!(audioresample = __create_element(DEFAULT_ELEMENT_AUDIORESAMPLE, NULL))) {
- LOG_ERROR("failed to create audioresample");
- return WEBRTC_ERROR_INVALID_OPERATION;
- }
-
- /* FIXME: get factory name from ini */
- if (!(audiosink = __create_element(DEFAULT_ELEMENT_AUDIOSINK, NULL))) {
- LOG_ERROR("failed to create audiosink");
- return WEBRTC_ERROR_INVALID_OPERATION;
- }
-
- gst_bin_add_many(GST_BIN(sink->bin), audioconvert, audioresample, audiosink, NULL);
-
- if (!gst_element_sync_state_with_parent(audioconvert)) {
- LOG_ERROR("failed to gst_element_sync_state_with_parent() for [%s]", GST_ELEMENT_NAME(audioconvert));
- return WEBRTC_ERROR_INVALID_OPERATION;
- }
- if (!gst_element_sync_state_with_parent(audioresample)) {
- LOG_ERROR("failed to gst_element_sync_state_with_parent() for [%s]", GST_ELEMENT_NAME(audioresample));
- return WEBRTC_ERROR_INVALID_OPERATION;
- }
- if (!gst_element_sync_state_with_parent(audiosink)) {
- LOG_ERROR("failed to gst_element_sync_state_with_parent() for [%s]", GST_ELEMENT_NAME(audiosink));
- return WEBRTC_ERROR_INVALID_OPERATION;
- }
-
- if (!gst_element_link_many(decodebin, audioconvert, audioresample, audiosink, NULL)) {
- LOG_ERROR("failed to gst_element_link_many()");
- return WEBRTC_ERROR_INVALID_OPERATION;
- }
-
- return WEBRTC_ERROR_NONE;
-}
-
-static void __decodebin_pad_added_cb(GstElement *decodebin, GstPad *new_pad, gpointer user_data)
-{
- int ret = WEBRTC_ERROR_NONE;
- webrtc_s *webrtc = (webrtc_s *)user_data;
- const gchar *media_type;
-
- RET_IF(webrtc == NULL, "webrtc is NULL");
-
- if (GST_PAD_DIRECTION(new_pad) != GST_PAD_SRC)
- return;
-
- media_type = gst_structure_get_name(gst_caps_get_structure(gst_pad_get_current_caps(new_pad), 0));
- LOG_INFO("[%s], new_pad[%s], media_type[%s]", GST_ELEMENT_NAME(decodebin), GST_PAD_NAME(new_pad), media_type);
-
- if (g_strrstr(media_type, "video")) {
- ret = __build_videosink(webrtc, decodebin, new_pad);
-
- } else if (g_strrstr(media_type, "audio")) {
- ret = __build_audiosink(webrtc, decodebin, new_pad);
-
- } else {
- LOG_ERROR("not supported media type[%s]", media_type);
- return;
- }
-
- if (ret != WEBRTC_ERROR_NONE)
- LOG_ERROR("failed to build a rendering pipeline");
-
- GENERATE_DOT(webrtc, "%s", GST_ELEMENT_NAME(decodebin));
-}
-
-static int __decodebin_autoplug_select_cb(GstElement *decodebin, GstPad *pad, GstCaps *caps, GstElementFactory *factory, gpointer user_data)
-{
- /* NOTE : GstAutoplugSelectResult is defined in gstplay-enum.h but not exposed */
- typedef enum {
- GST_AUTOPLUG_SELECT_TRY,
- GST_AUTOPLUG_SELECT_EXPOSE,
- GST_AUTOPLUG_SELECT_SKIP
- } GstAutoplugSelectResult;
- gchar *factory_name;
- const gchar *klass;
- GstAutoplugSelectResult result = GST_AUTOPLUG_SELECT_TRY;
- webrtc_s *webrtc = (webrtc_s *)user_data;
-
- RET_VAL_IF(webrtc == NULL, GST_AUTOPLUG_SELECT_SKIP, "webrtc is NULL, skip it");
-
- factory_name = GST_OBJECT_NAME(factory);
- klass = gst_element_factory_get_metadata(factory, GST_ELEMENT_METADATA_KLASS);
-
- LOG_INFO("factory [name:%s, klass:%s]", factory_name, klass);
-
- return result;
-}
-
-static unsigned int __get_id_from_pad_name(const gchar* name)
-{
- gchar **tokens = NULL;
- gint64 id;
-
- RET_VAL_IF(name == NULL, 0, "name is NULL");
-
- tokens = g_strsplit(name, "_", 2);
- id = g_ascii_strtoll(tokens[1], NULL, 10);
-
- g_strfreev(tokens);
-
- return (unsigned int)id;
-}
-
static GstPad* __add_no_target_ghostpad(GstElement *bin, const char *pad_name, bool is_src)
{
gchar *bin_name = NULL;
return ghost_pad;
}
-static int __add_no_target_ghostpad_to_slot(webrtc_gst_slot_s *slot, gboolean is_src, GstPad **new_pad)
+int _add_no_target_ghostpad_to_slot(webrtc_gst_slot_s *slot, gboolean is_src, GstPad **new_pad)
{
gchar *pad_name;
return WEBRTC_ERROR_NONE;
}
-static int __set_ghost_pad_target(GstPad *ghost_pad, GstElement *target_element, gboolean is_src)
+int _set_ghost_pad_target(GstPad *ghost_pad, GstElement *target_element, gboolean is_src)
{
GstPad *target_pad;
return WEBRTC_ERROR_NONE;
}
-static int __add_rendering_sink_bin(webrtc_s *webrtc, GstPad *src_pad)
-{
- int ret = WEBRTC_ERROR_NONE;
- unsigned int id;
- gchar *bin_name;
- gchar *decodebin_name;
- webrtc_gst_slot_s *sink;
- GstElement *decodebin;
- GstPad *sink_pad;
-
- RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
- RET_VAL_IF(src_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "src_pad is NULL");
-
- /* decodebin_name/sink will be freed by function which is set to g_hash_table_new_full() */
- id = __get_id_from_pad_name(GST_PAD_NAME(src_pad));
- bin_name = g_strdup_printf("rendering_sink_%u", id);
- decodebin_name = g_strdup_printf("%s_decodebin", bin_name);
-
- MALLOC_AND_INIT_SLOT(sink, id, bin_name);
-
- g_free(bin_name);
-
- decodebin = __create_element("decodebin", decodebin_name);
- if (!decodebin)
- goto error_before_insert;
-
- gst_bin_add(GST_BIN(sink->bin), decodebin);
-
- g_signal_connect(decodebin, "pad-added", G_CALLBACK(__decodebin_pad_added_cb), webrtc);
- g_signal_connect(decodebin, "autoplug-select", G_CALLBACK(__decodebin_autoplug_select_cb), webrtc);
-
- ret = __add_no_target_ghostpad_to_slot(sink, FALSE, &sink_pad);
- if (ret != WEBRTC_ERROR_NONE)
- goto error_before_insert;
-
- ret = __set_ghost_pad_target(sink_pad, decodebin, FALSE);
- if (ret != WEBRTC_ERROR_NONE)
- goto error_before_insert;
-
- if (!gst_bin_add(GST_BIN(webrtc->gst.pipeline), sink->bin)) {
- LOG_ERROR("failed to gst_bin_add(), [%s] -> [%s] pipeline", GST_ELEMENT_NAME(sink->bin), GST_ELEMENT_NAME(webrtc->gst.pipeline));
- goto error_before_insert;
- }
-
- if (gst_pad_link(src_pad, sink_pad) != GST_PAD_LINK_OK) {
- LOG_ERROR("failed to gst_pad_link(), %s:%s", GST_PAD_NAME(src_pad), GST_PAD_NAME(sink_pad));
- goto error_before_insert;
- }
-
- LOG_DEBUG("link pads successfully, [%s:%s] - [%s:%s]",
- GST_ELEMENT_NAME(webrtc->gst.webrtcbin), GST_PAD_NAME(src_pad), GST_ELEMENT_NAME(sink->bin), GST_PAD_NAME(sink_pad));
-
- if (!g_hash_table_insert(webrtc->gst.sink_slots, decodebin_name, (gpointer)sink)) {
- LOG_ERROR("should not be reached here, bin_name[%s] already exist, sink id[%u] will be removed", decodebin_name, sink->id);
- g_hash_table_remove(webrtc->gst.sink_slots, decodebin_name);
- return WEBRTC_ERROR_INVALID_OPERATION;
- }
-
- gst_element_sync_state_with_parent(sink->bin);
-
- LOG_INFO("added a sink slot[%p, id:%u]", sink, sink->id);
-
- return WEBRTC_ERROR_NONE;
-
-error_before_insert:
- gst_object_unref(sink_pad);
- g_free(decodebin_name);
- g_free(sink);
-
- return WEBRTC_ERROR_INVALID_OPERATION;
-}
-
static void __webrtcbin_pad_added_cb(GstElement *webrtcbin, GstPad *new_pad, gpointer user_data)
{
int ret = WEBRTC_ERROR_NONE;
LOG_INFO("new pad[%s] is added", GST_PAD_NAME(new_pad));
- ret = __add_rendering_sink_bin(webrtc, new_pad);
- RET_IF(ret != WEBRTC_ERROR_NONE, "failed to __add_rendering_sink_bin()");
+ ret = _add_rendering_sink_bin(webrtc, new_pad);
+ RET_IF(ret != WEBRTC_ERROR_NONE, "failed to _add_rendering_sink_bin()");
GENERATE_DOT(webrtc, "webrtcbin_%s", GST_PAD_NAME(new_pad));
}
goto error;
}
- if (!(webrtc->gst.webrtcbin = __create_element("webrtcbin", NULL))) {
+ if (!(webrtc->gst.webrtcbin = _create_element("webrtcbin", NULL))) {
LOG_ERROR("failed to create webrtcbin");
goto error;
}
return WEBRTC_ERROR_NONE;
}
-static GstCaps *__make_default_raw_caps(webrtc_media_source_type_e type)
-{
- GstCaps *caps = NULL;
-
- switch (type) {
- case WEBRTC_MEDIA_SOURCE_TYPE_CAMERA:
- case WEBRTC_MEDIA_SOURCE_TYPE_VIDEOTEST:
- /* FIXME: get default value from ini */
- caps = gst_caps_new_simple(DEFAULT_VIDEO_RAW_MEDIA_TYPE,
- "format", G_TYPE_STRING, DEFAULT_VIDEO_RAW_FORMAT,
- "width", G_TYPE_INT, DEFAULT_VIDEO_WIDTH,
- "height", G_TYPE_INT, DEFAULT_VIDEO_HEIGHT,
- NULL);
- break;
-
- case WEBRTC_MEDIA_SOURCE_TYPE_MIC:
- case WEBRTC_MEDIA_SOURCE_TYPE_AUDIOTEST:
- /* FIXME: get default value from ini */
- caps = gst_caps_new_simple(DEFAULT_AUDIO_RAW_MEDIA_TYPE,
- "format", G_TYPE_STRING, DEFAULT_AUDIO_RAW_FORMAT,
- "channels", G_TYPE_INT, DEFAULT_AUDIO_CHANNELS,
- "rate", G_TYPE_INT, DEFAULT_AUDIO_SAMPLERATE,
- NULL);
- break;
-
- default:
- LOG_ERROR_IF_REACHED("type(%d)", type);
- break;
- }
-
- return caps;
-}
-
-/* Use g_free() to free the media_type parameter. */
-static GstCaps *__make_default_encoded_caps(webrtc_media_source_type_e type, gchar **media_type)
-{
- GstCaps *caps;
- const char *_media_type;
-
- switch (type) {
- case WEBRTC_MEDIA_SOURCE_TYPE_CAMERA:
- case WEBRTC_MEDIA_SOURCE_TYPE_VIDEOTEST:
- /* FIXME: get default value from ini */
- _media_type = DEFAULT_VIDEO_ENCODED_MEDIA_TYPE;
- caps = gst_caps_new_simple(_media_type,
- "width", G_TYPE_INT, DEFAULT_VIDEO_WIDTH,
- "height", G_TYPE_INT, DEFAULT_VIDEO_HEIGHT,
- NULL);
- break;
-
- case WEBRTC_MEDIA_SOURCE_TYPE_MIC:
- case WEBRTC_MEDIA_SOURCE_TYPE_AUDIOTEST:
- /* FIXME: get default value from ini */
- _media_type = DEFAULT_AUDIO_ENCODED_MEDIA_TYPE;
- caps = gst_caps_new_simple(DEFAULT_AUDIO_ENCODED_MEDIA_TYPE,
- "channels", G_TYPE_INT, DEFAULT_AUDIO_CHANNELS,
- "rate", G_TYPE_INT, DEFAULT_AUDIO_SAMPLERATE,
- NULL);
- break;
-
- default:
- LOG_ERROR_IF_REACHED("type(%d)", type);
- return NULL;
- }
-
- if (media_type)
- *media_type = g_strdup(_media_type);
-
- return caps;
-}
-
-static codec_type_e __get_codec_type(const gchar *media_type)
-{
- if (!g_strcmp0(media_type, "audio/x-opus"))
- return CODEC_TYPE_OPUS;
- else if (!g_strcmp0(media_type, "audio/x-vorbis"))
- return CODEC_TYPE_VORBIS;
- else if (!g_strcmp0(media_type, "video/x-vp8"))
- return CODEC_TYPE_VP8;
- else if (!g_strcmp0(media_type, "video/x-vp9"))
- return CODEC_TYPE_VP9;
- else if (!g_strcmp0(media_type, "video/x-theora"))
- return CODEC_TYPE_THEORA;
- else if (!g_strcmp0(media_type, "video/x-h263"))
- return CODEC_TYPE_H263;
- else if (!g_strcmp0(media_type, "video/x-h264"))
- return CODEC_TYPE_H264;
- else if (!g_strcmp0(media_type, "video/x-h265"))
- return CODEC_TYPE_H265;
- else
- return CODEC_TYPE_NOT_SUPPORTED;
-}
-
-static GstCaps *__make_rtp_caps(const gchar *media_type, unsigned int id)
-{
- gchar *caps_str;
- GstCaps *caps;
- codec_type_e codec_type = __get_codec_type(media_type);
-
- RET_VAL_IF(codec_type == CODEC_TYPE_NOT_SUPPORTED, NULL, "media_type[%s] is not supported", media_type);
-
- caps = gst_caps_new_simple("application/x-rtp",
- "media", G_TYPE_STRING, g_strrstr(media_type, "video") ? "video" : "audio",
- "clock-rate", G_TYPE_INT, payload_types[codec_type].clock_rate, /* FIXME: support various clock-rate */
- "encoding-name", G_TYPE_STRING, payload_types[codec_type].encoding_name,
- "payload", G_TYPE_INT, id + 95, NULL);
-
- caps_str = gst_caps_to_string(caps);
- LOG_DEBUG("RTP caps is created [%s]", caps_str);
-
- g_free(caps_str);
-
- return caps;
-}
-
-static int __create_rest_of_elements(webrtc_gst_slot_s *source, webrtc_media_source_type_e type, GstElement **capsfilter, GstElement **encoder, GstElement **payloader, GstElement **queue, GstElement **capsfilter2)
-{
- GstCaps *sink_caps;
- element_info_s elem_info;
- const gchar *encoder_klass_name;
- gchar *media_type;
-
- RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL");
- RET_VAL_IF(capsfilter == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "capsfilter is NULL");
- RET_VAL_IF(encoder == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "encoder is NULL");
- RET_VAL_IF(payloader == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "payloader is NULL");
- RET_VAL_IF(queue == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "queue is NULL");
- RET_VAL_IF(capsfilter2 == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "capsfilter2 is NULL");
-
- if (!(*capsfilter = __create_element(DEFAULT_ELEMENT_CAPSFILTER, NULL))) {
- LOG_ERROR("failed to create capsfilter");
- return WEBRTC_ERROR_INVALID_OPERATION;
- }
- if ((sink_caps = __make_default_raw_caps(type))) {
- g_object_set(G_OBJECT(*capsfilter), "caps", sink_caps, NULL);
- gst_caps_unref(sink_caps);
- }
-
- if (type == WEBRTC_MEDIA_SOURCE_TYPE_CAMERA || type == WEBRTC_MEDIA_SOURCE_TYPE_VIDEOTEST)
- encoder_klass_name = GST_KLASS_NAME_ENCODER_VIDEO;
- else
- encoder_klass_name = GST_KLASS_NAME_ENCODER_AUDIO;
-
- CREATE_ELEMENT_FROM_REGISTRY(elem_info, encoder_klass_name,
- __make_default_raw_caps(type),
- __make_default_encoded_caps(type, NULL),
- *encoder);
-
- CREATE_ELEMENT_FROM_REGISTRY(elem_info, GST_KLASS_NAME_PAYLOADER_RTP,
- __make_default_encoded_caps(type, &media_type),
- NULL,
- *payloader);
-
- if (!(*queue = __create_element(DEFAULT_ELEMENT_QUEUE, NULL))) {
- LOG_ERROR("failed to create queue");
- g_free(media_type);
- return WEBRTC_ERROR_INVALID_OPERATION;
- }
-
- if (!(*capsfilter2 = __create_element(DEFAULT_ELEMENT_CAPSFILTER, NULL))) {
- LOG_ERROR("failed to create capsfilter");
- g_free(media_type);
- return WEBRTC_ERROR_INVALID_OPERATION;
- }
- if ((sink_caps = __make_rtp_caps(media_type, source->id))) {
- g_object_set(G_OBJECT(*capsfilter2), "caps", sink_caps, NULL);
- gst_caps_unref(sink_caps);
- }
-
- g_free(media_type);
-
- return WEBRTC_ERROR_NONE;
-}
-
-static int __build_camerasrc(webrtc_gst_slot_s *source, GstPad *ghost_src_pad)
-{
- int ret = WEBRTC_ERROR_NONE;
- GstElement *camerasrc;
- GstElement *capsfilter;
- GstElement *videoenc;
- GstElement *videopay;
- GstElement *queue;
- GstElement *capsfilter2;
-
- RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL");
- RET_VAL_IF(ghost_src_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "ghost_src_pad is NULL");
- RET_VAL_IF(source->bin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "bin is NULL");
-
- source->media_types |= MEDIA_TYPE_VIDEO;
-
- /* FIXME: get factory name from ini */
- if (!(camerasrc = __create_element(DEFAULT_ELEMENT_CAMERASRC, NULL))) {
- LOG_ERROR("failed to create camerasrc");
- return WEBRTC_ERROR_INVALID_OPERATION;
- }
- /* FIXME: set camera default setting from ini */
-
- if ((ret = __create_rest_of_elements(source, WEBRTC_MEDIA_SOURCE_TYPE_CAMERA, &capsfilter, &videoenc, &videopay, &queue, &capsfilter2)) != WEBRTC_ERROR_NONE)
- return ret;
-
- gst_bin_add_many(GST_BIN(source->bin), camerasrc, capsfilter, videoenc, videopay, queue, capsfilter2, NULL);
- if (!gst_element_link_many(camerasrc, capsfilter, videoenc, videopay, queue, capsfilter2, NULL)) {
- LOG_ERROR("failed to gst_element_link_many()");
- return WEBRTC_ERROR_INVALID_OPERATION;
- }
-
- return __set_ghost_pad_target(ghost_src_pad, capsfilter2, TRUE);
-}
-
-static int __build_audiosrc(webrtc_gst_slot_s *source, GstPad *ghost_src_pad)
-{
- int ret = WEBRTC_ERROR_NONE;
- GstElement *audiosrc;
- GstElement *capsfilter;
- GstElement *audioenc;
- GstElement *audiopay;
- GstElement *queue;
- GstElement *capsfilter2;
-
- RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL");
- RET_VAL_IF(ghost_src_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "ghost_src_pad is NULL");
- RET_VAL_IF(source->bin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "bin is NULL");
-
- source->media_types |= MEDIA_TYPE_AUDIO;
-
- if (!(audiosrc = __create_element(DEFAULT_ELEMENT_AUDIOSRC, NULL))) {
- LOG_ERROR("failed to create audiosrc");
- return WEBRTC_ERROR_INVALID_OPERATION;
- }
-
- if ((ret = __create_rest_of_elements(source, WEBRTC_MEDIA_SOURCE_TYPE_MIC, &capsfilter, &audioenc, &audiopay, &queue, &capsfilter2)) != WEBRTC_ERROR_NONE)
- return ret;
-
- gst_bin_add_many(GST_BIN(source->bin), audiosrc, capsfilter, audioenc, audiopay, queue, capsfilter2, NULL);
- if (!gst_element_link_many(audiosrc, capsfilter, audioenc, audiopay, queue, capsfilter2, NULL)) {
- LOG_ERROR("failed to gst_element_link_many()");
- return WEBRTC_ERROR_INVALID_OPERATION;
- }
-
- return __set_ghost_pad_target(ghost_src_pad, capsfilter2, TRUE);
-}
-
-static int __build_videotestsrc(webrtc_gst_slot_s *source, GstPad *ghost_src_pad)
-{
- int ret = WEBRTC_ERROR_NONE;
- GstElement *videotestsrc;
- GstElement *capsfilter;
- GstElement *videoenc;
- GstElement *videopay;
- GstElement *queue;
- GstElement *capsfilter2;
-
- RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL");
- RET_VAL_IF(ghost_src_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "ghost_src_pad is NULL");
- RET_VAL_IF(source->bin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "bin is NULL");
-
- source->media_types |= MEDIA_TYPE_VIDEO;
-
- if (!(videotestsrc = __create_element(DEFAULT_ELEMENT_VIDEOTESTSRC, NULL))) {
- LOG_ERROR("failed to create videotestsrc");
- return WEBRTC_ERROR_INVALID_OPERATION;
- }
- g_object_set(G_OBJECT(videotestsrc), "is-live", TRUE, NULL);
-
- if ((ret = __create_rest_of_elements(source, WEBRTC_MEDIA_SOURCE_TYPE_VIDEOTEST, &capsfilter, &videoenc, &videopay, &queue, &capsfilter2)) != WEBRTC_ERROR_NONE)
- return ret;
-
- gst_bin_add_many(GST_BIN(source->bin), videotestsrc, capsfilter, videoenc, videopay, queue, capsfilter2, NULL);
- if (!gst_element_link_many(videotestsrc, capsfilter, videoenc, videopay, queue, capsfilter2, NULL)) {
- LOG_ERROR("failed to gst_element_link_many()");
- return WEBRTC_ERROR_INVALID_OPERATION;
- }
-
- return __set_ghost_pad_target(ghost_src_pad, capsfilter2, TRUE);
-}
-
-static int __build_audiotestsrc(webrtc_gst_slot_s *source, GstPad *ghost_src_pad)
-{
- int ret = WEBRTC_ERROR_NONE;
- GstElement *audiotestsrc;
- GstElement *capsfilter;
- GstElement *audioenc;
- GstElement *audiopay;
- GstElement *queue;
- GstElement *capsfilter2;
-
- RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL");
- RET_VAL_IF(ghost_src_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "ghost_src_pad is NULL");
- RET_VAL_IF(source->bin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "bin is NULL");
-
- source->media_types |= MEDIA_TYPE_AUDIO;
-
- if (!(audiotestsrc = __create_element(DEFAULT_ELEMENT_AUDIOTESTSRC, NULL))) {
- LOG_ERROR("failed to create audiotestsrc");
- return WEBRTC_ERROR_INVALID_OPERATION;
- }
- g_object_set(G_OBJECT(audiotestsrc), "is-live", TRUE, NULL);
-
- if ((ret = __create_rest_of_elements(source, WEBRTC_MEDIA_SOURCE_TYPE_AUDIOTEST, &capsfilter, &audioenc, &audiopay, &queue, &capsfilter2)) != WEBRTC_ERROR_NONE)
- return ret;
-
- gst_bin_add_many(GST_BIN(source->bin), audiotestsrc, capsfilter, audioenc, audiopay, queue, capsfilter2, NULL);
- if (!gst_element_link_many(audiotestsrc, capsfilter, audioenc, audiopay, queue, capsfilter2, NULL)) {
- LOG_ERROR("failed to gst_element_link_many()");
- return WEBRTC_ERROR_INVALID_OPERATION;
- }
-
- return __set_ghost_pad_target(ghost_src_pad, capsfilter2, TRUE);
-}
-
-static int __build_source_bin(webrtc_gst_slot_s *source, webrtc_media_source_type_e type)
-{
- int ret = WEBRTC_ERROR_NONE;
- GstPad *src_pad;
-
- 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");
-
- 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()");
-
- switch (type) {
- case WEBRTC_MEDIA_SOURCE_TYPE_CAMERA:
- return __build_camerasrc(source, src_pad);
-
- case WEBRTC_MEDIA_SOURCE_TYPE_MIC:
- return __build_audiosrc(source, src_pad);
-
- case WEBRTC_MEDIA_SOURCE_TYPE_VIDEOTEST:
- return __build_videotestsrc(source, src_pad);
-
- case WEBRTC_MEDIA_SOURCE_TYPE_AUDIOTEST:
- return __build_audiotestsrc(source, src_pad);
-
- default:
- LOG_ERROR_IF_REACHED("type(%d)", type);
- return WEBRTC_ERROR_INVALID_PARAMETER;
- }
-
- return WEBRTC_ERROR_NONE;
-}
-
-static unsigned int __get_unoccupied_id(GHashTable *slots)
-{
- int i;
- gchar *key;
-
- RET_VAL_IF(slots == NULL, 0, "slot is NULL");
-
- /* Payload identifiers 96–127 are used for payloads defined dynamically during a session,
- * hence the id range is limited here to 1-32. */
- for (i = 1; i < 33; i++) {
- key = g_strdup_printf("media_source_%u", i);
- if (g_hash_table_contains(slots, key)) {
- g_free(key);
- continue;
- }
- g_free(key);
- return i;
- }
-
- LOG_ERROR("all slots are occupied(1-32)");
-
- return 0;
-}
-
-int _add_media_source(webrtc_s *webrtc, webrtc_media_source_type_e type, unsigned int *source_id)
-{
- int ret = WEBRTC_ERROR_NONE;
- unsigned int id;
- webrtc_gst_slot_s *source = NULL;
- gchar *bin_name = NULL;
- GstPad *webrtc_sinkpad;
- gchar *webrtc_sinkpad_name;
- gchar *bin_srcpad_name = NULL;
-
- RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
- RET_VAL_IF(webrtc->gst.source_slots == NULL, WEBRTC_ERROR_INVALID_OPERATION, "source_slots is NULL");
- RET_VAL_IF(source_id == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source_id is NULL");
-
- /* bin_name/source will be freed by function which is set to g_hash_table_new_full() */
- id = __get_unoccupied_id(webrtc->gst.source_slots);
- RET_VAL_IF(id == 0, WEBRTC_ERROR_INVALID_OPERATION, "source_slots are full");
-
- bin_name = g_strdup_printf("media_source_%u", id);
-
- MALLOC_AND_INIT_SLOT(source, id, bin_name);
-
- ret = __build_source_bin(source, type);
- if (ret != WEBRTC_ERROR_NONE) {
- LOG_ERROR("failed to __build_source_bin()");
- goto error;
- }
-
- if (!gst_bin_add(GST_BIN(webrtc->gst.pipeline), source->bin)) {
- LOG_ERROR("failed to gst_bin_add(), [%s] -> [%s] pipeline", GST_ELEMENT_NAME(source->bin), GST_ELEMENT_NAME(webrtc->gst.pipeline));
- goto error;
- }
-
- /* The gst_element_get_request_pad() of webrtcbin will trigger the transciever callback. To update the mline value of
- * new transceiver object to the source structure in the callback, hash table inserting should be preceded. */
- if (!g_hash_table_insert(webrtc->gst.source_slots, bin_name, (gpointer)source)) {
- LOG_ERROR("should not be reached here, bin_name[%s] already exist, source id[%u] will be removed", bin_name, source->id);
- g_hash_table_remove(webrtc->gst.source_slots, bin_name);
- return WEBRTC_ERROR_INVALID_OPERATION;
- }
-
- if (!(webrtc_sinkpad = gst_element_get_request_pad(webrtc->gst.webrtcbin, "sink_%u"))) {
- LOG_ERROR("failed to gst_element_get_request_pad()");
- goto error_after_insert;
- }
- if (!(webrtc_sinkpad_name = gst_pad_get_name(webrtc_sinkpad))) {
- LOG_ERROR("failed to gst_pad_get_name()");
- goto error_after_insert;
- }
- bin_srcpad_name = g_strdup_printf("src_%u", id);
- if (!gst_element_link_pads(source->bin, bin_srcpad_name, webrtc->gst.webrtcbin, webrtc_sinkpad_name)) {
- LOG_ERROR("failed to link pads, [%s:%s] - [%s:%s]",
- GST_ELEMENT_NAME(source->bin), bin_srcpad_name, GST_ELEMENT_NAME(webrtc->gst.webrtcbin), webrtc_sinkpad_name);
- goto error_after_insert;
- }
- LOG_DEBUG("link pads successfully, [%s:%s] - [%s:%s]",
- GST_ELEMENT_NAME(source->bin), bin_srcpad_name, GST_ELEMENT_NAME(webrtc->gst.webrtcbin), webrtc_sinkpad_name);
-
- *source_id = id;
-
- LOG_INFO("added a source slot[%p, id:%u]", source, source->id);
-
- g_free(bin_srcpad_name);
-
- return WEBRTC_ERROR_NONE;
-
-error_after_insert:
- g_hash_table_remove(webrtc->gst.source_slots, bin_name);
- g_free(bin_srcpad_name);
-
- return WEBRTC_ERROR_INVALID_OPERATION;
-
-error:
- g_free(bin_name);
- g_free(source);
-
- return WEBRTC_ERROR_INVALID_OPERATION;
-}
-
-int _remove_media_source(webrtc_s *webrtc, unsigned int source_id)
-{
- int ret = WEBRTC_ERROR_NONE;
- gchar *bin_name = NULL;
-
- RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
- RET_VAL_IF(webrtc->gst.source_slots == NULL, WEBRTC_ERROR_INVALID_OPERATION, "source_slots is NULL");
-
- bin_name = g_strdup_printf("media_source_%u", source_id);
-
- if (!g_hash_table_remove(webrtc->gst.source_slots, (gpointer)bin_name)) {
- LOG_ERROR("failed to find media source by id[%u]", source_id);
- ret = WEBRTC_ERROR_INVALID_PARAMETER;
- }
-
- g_free(bin_name);
-
- return ret;
-}
-
static gboolean __check_id_equal_cb(gpointer key, gpointer value, gpointer user_data)
{
webrtc_gst_slot_s *slot = value;
--- /dev/null
+/*
+ * Copyright (c) 2020 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"
+
+#define DEFAULT_ELEMENT_VIDEOCONVERT "videoconvert"
+#define DEFAULT_ELEMENT_AUDIOCONVERT "audioconvert"
+#define DEFAULT_ELEMENT_AUDIORESAMPLE "audioresample"
+#define DEFAULT_ELEMENT_VIDEOSINK "tizenwlsink"
+#define DEFAULT_ELEMENT_AUDIOSINK "pulsesink"
+
+static webrtc_gst_slot_s* __find_sink_slot(webrtc_s *webrtc, const gchar *key)
+{
+ RET_VAL_IF(webrtc == NULL, NULL, "webrtc is NULL");
+ RET_VAL_IF(key == NULL, NULL, "key is NULL");
+
+ return g_hash_table_lookup(webrtc->gst.sink_slots, key);
+}
+
+static int __build_videosink(webrtc_s *webrtc, GstElement *decodebin, GstPad *src_pad)
+{
+ webrtc_gst_slot_s *sink;
+ GstElement *videoconvert;
+ GstElement *videosink;
+
+ RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
+ RET_VAL_IF(decodebin == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "decodebin is NULL");
+ RET_VAL_IF(src_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "src_pad is NULL");
+
+ sink = __find_sink_slot(webrtc, GST_ELEMENT_NAME(decodebin));
+ RET_VAL_IF(sink == NULL, WEBRTC_ERROR_INVALID_OPERATION, "could not find an item by [%s] in sink slots", GST_ELEMENT_NAME(decodebin));
+ RET_VAL_IF(sink->bin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "bin is NULL");
+
+ sink->media_types |= MEDIA_TYPE_VIDEO;
+
+ if (!(videoconvert = _create_element(DEFAULT_ELEMENT_VIDEOCONVERT, NULL))) {
+ LOG_ERROR("failed to create videoconvert");
+ return WEBRTC_ERROR_INVALID_OPERATION;
+ }
+
+ /* FIXME: get factory name from ini */
+ if (!(videosink = _create_element(DEFAULT_ELEMENT_VIDEOSINK, NULL))) {
+ LOG_ERROR("failed to create videosink");
+ return WEBRTC_ERROR_INVALID_OPERATION;
+ }
+
+ gst_bin_add_many(GST_BIN(sink->bin), videoconvert, videosink, NULL);
+
+ if (!gst_element_sync_state_with_parent(videoconvert)) {
+ LOG_ERROR("failed to gst_element_sync_state_with_parent() for [%s]", GST_ELEMENT_NAME(videoconvert));
+ return WEBRTC_ERROR_INVALID_OPERATION;
+ }
+ if (!gst_element_sync_state_with_parent(videosink)) {
+ LOG_ERROR("failed to gst_element_sync_state_with_parent() for [%s]", GST_ELEMENT_NAME(videosink));
+ return WEBRTC_ERROR_INVALID_OPERATION;
+ }
+
+ if (!gst_element_link_many(decodebin, videoconvert, videosink, NULL)) {
+ LOG_ERROR("failed to gst_element_link_many()");
+ return WEBRTC_ERROR_INVALID_OPERATION;
+ }
+
+ return WEBRTC_ERROR_NONE;
+}
+
+static int __build_audiosink(webrtc_s *webrtc, GstElement *decodebin, GstPad *src_pad)
+{
+ webrtc_gst_slot_s *sink;
+ GstElement *audioconvert;
+ GstElement *audioresample;
+ GstElement *audiosink;
+
+ RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
+ RET_VAL_IF(decodebin == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "decodebin is NULL");
+ RET_VAL_IF(src_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "src_pad is NULL");
+
+ sink = __find_sink_slot(webrtc, GST_ELEMENT_NAME(decodebin));
+ RET_VAL_IF(sink == NULL, WEBRTC_ERROR_INVALID_OPERATION, "could not find an item by [%s] in sink slots", GST_ELEMENT_NAME(decodebin));
+ RET_VAL_IF(sink->bin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "bin is NULL");
+
+ sink->media_types |= MEDIA_TYPE_AUDIO;
+
+ if (!(audioconvert = _create_element(DEFAULT_ELEMENT_AUDIOCONVERT, NULL))) {
+ LOG_ERROR("failed to create audioconvert");
+ return WEBRTC_ERROR_INVALID_OPERATION;
+ }
+
+ if (!(audioresample = _create_element(DEFAULT_ELEMENT_AUDIORESAMPLE, NULL))) {
+ LOG_ERROR("failed to create audioresample");
+ return WEBRTC_ERROR_INVALID_OPERATION;
+ }
+
+ /* FIXME: get factory name from ini */
+ if (!(audiosink = _create_element(DEFAULT_ELEMENT_AUDIOSINK, NULL))) {
+ LOG_ERROR("failed to create audiosink");
+ return WEBRTC_ERROR_INVALID_OPERATION;
+ }
+
+ gst_bin_add_many(GST_BIN(sink->bin), audioconvert, audioresample, audiosink, NULL);
+
+ if (!gst_element_sync_state_with_parent(audioconvert)) {
+ LOG_ERROR("failed to gst_element_sync_state_with_parent() for [%s]", GST_ELEMENT_NAME(audioconvert));
+ return WEBRTC_ERROR_INVALID_OPERATION;
+ }
+ if (!gst_element_sync_state_with_parent(audioresample)) {
+ LOG_ERROR("failed to gst_element_sync_state_with_parent() for [%s]", GST_ELEMENT_NAME(audioresample));
+ return WEBRTC_ERROR_INVALID_OPERATION;
+ }
+ if (!gst_element_sync_state_with_parent(audiosink)) {
+ LOG_ERROR("failed to gst_element_sync_state_with_parent() for [%s]", GST_ELEMENT_NAME(audiosink));
+ return WEBRTC_ERROR_INVALID_OPERATION;
+ }
+
+ if (!gst_element_link_many(decodebin, audioconvert, audioresample, audiosink, NULL)) {
+ LOG_ERROR("failed to gst_element_link_many()");
+ return WEBRTC_ERROR_INVALID_OPERATION;
+ }
+
+ return WEBRTC_ERROR_NONE;
+}
+
+static void __decodebin_pad_added_cb(GstElement *decodebin, GstPad *new_pad, gpointer user_data)
+{
+ int ret = WEBRTC_ERROR_NONE;
+ webrtc_s *webrtc = (webrtc_s *)user_data;
+ const gchar *media_type;
+
+ RET_IF(webrtc == NULL, "webrtc is NULL");
+
+ if (GST_PAD_DIRECTION(new_pad) != GST_PAD_SRC)
+ return;
+
+ media_type = gst_structure_get_name(gst_caps_get_structure(gst_pad_get_current_caps(new_pad), 0));
+ LOG_INFO("[%s], new_pad[%s], media_type[%s]", GST_ELEMENT_NAME(decodebin), GST_PAD_NAME(new_pad), media_type);
+
+ if (g_strrstr(media_type, "video")) {
+ ret = __build_videosink(webrtc, decodebin, new_pad);
+
+ } else if (g_strrstr(media_type, "audio")) {
+ ret = __build_audiosink(webrtc, decodebin, new_pad);
+
+ } else {
+ LOG_ERROR("not supported media type[%s]", media_type);
+ return;
+ }
+
+ if (ret != WEBRTC_ERROR_NONE)
+ LOG_ERROR("failed to build a rendering pipeline");
+
+ GENERATE_DOT(webrtc, "%s", GST_ELEMENT_NAME(decodebin));
+}
+
+static int __decodebin_autoplug_select_cb(GstElement *decodebin, GstPad *pad, GstCaps *caps, GstElementFactory *factory, gpointer user_data)
+{
+ /* NOTE : GstAutoplugSelectResult is defined in gstplay-enum.h but not exposed */
+ typedef enum {
+ GST_AUTOPLUG_SELECT_TRY,
+ GST_AUTOPLUG_SELECT_EXPOSE,
+ GST_AUTOPLUG_SELECT_SKIP
+ } GstAutoplugSelectResult;
+ gchar *factory_name;
+ const gchar *klass;
+ GstAutoplugSelectResult result = GST_AUTOPLUG_SELECT_TRY;
+ webrtc_s *webrtc = (webrtc_s *)user_data;
+
+ RET_VAL_IF(webrtc == NULL, GST_AUTOPLUG_SELECT_SKIP, "webrtc is NULL, skip it");
+
+ factory_name = GST_OBJECT_NAME(factory);
+ klass = gst_element_factory_get_metadata(factory, GST_ELEMENT_METADATA_KLASS);
+
+ LOG_INFO("factory [name:%s, klass:%s]", factory_name, klass);
+
+ return result;
+}
+
+static unsigned int __get_id_from_pad_name(const gchar* name)
+{
+ gchar **tokens = NULL;
+ gint64 id;
+
+ RET_VAL_IF(name == NULL, 0, "name is NULL");
+
+ tokens = g_strsplit(name, "_", 2);
+ id = g_ascii_strtoll(tokens[1], NULL, 10);
+
+ g_strfreev(tokens);
+
+ return (unsigned int)id;
+}
+
+int _add_rendering_sink_bin(webrtc_s *webrtc, GstPad *src_pad)
+{
+ int ret = WEBRTC_ERROR_NONE;
+ unsigned int id;
+ gchar *bin_name;
+ gchar *decodebin_name;
+ webrtc_gst_slot_s *sink;
+ GstElement *decodebin;
+ GstPad *sink_pad;
+
+ RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
+ RET_VAL_IF(src_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "src_pad is NULL");
+
+ /* decodebin_name/sink will be freed by function which is set to g_hash_table_new_full() */
+ id = __get_id_from_pad_name(GST_PAD_NAME(src_pad));
+ bin_name = g_strdup_printf("rendering_sink_%u", id);
+ decodebin_name = g_strdup_printf("%s_decodebin", bin_name);
+
+ MALLOC_AND_INIT_SLOT(sink, id, bin_name);
+
+ g_free(bin_name);
+
+ decodebin = _create_element("decodebin", decodebin_name);
+ if (!decodebin)
+ goto error_before_insert;
+
+ gst_bin_add(GST_BIN(sink->bin), decodebin);
+
+ g_signal_connect(decodebin, "pad-added", G_CALLBACK(__decodebin_pad_added_cb), webrtc);
+ g_signal_connect(decodebin, "autoplug-select", G_CALLBACK(__decodebin_autoplug_select_cb), webrtc);
+
+ ret = _add_no_target_ghostpad_to_slot(sink, FALSE, &sink_pad);
+ if (ret != WEBRTC_ERROR_NONE)
+ goto error_before_insert;
+
+ ret = _set_ghost_pad_target(sink_pad, decodebin, FALSE);
+ if (ret != WEBRTC_ERROR_NONE)
+ goto error_before_insert;
+
+ if (!gst_bin_add(GST_BIN(webrtc->gst.pipeline), sink->bin)) {
+ LOG_ERROR("failed to gst_bin_add(), [%s] -> [%s] pipeline", GST_ELEMENT_NAME(sink->bin), GST_ELEMENT_NAME(webrtc->gst.pipeline));
+ goto error_before_insert;
+ }
+
+ if (gst_pad_link(src_pad, sink_pad) != GST_PAD_LINK_OK) {
+ LOG_ERROR("failed to gst_pad_link(), %s:%s", GST_PAD_NAME(src_pad), GST_PAD_NAME(sink_pad));
+ goto error_before_insert;
+ }
+
+ LOG_DEBUG("link pads successfully, [%s:%s] - [%s:%s]",
+ GST_ELEMENT_NAME(webrtc->gst.webrtcbin), GST_PAD_NAME(src_pad), GST_ELEMENT_NAME(sink->bin), GST_PAD_NAME(sink_pad));
+
+ if (!g_hash_table_insert(webrtc->gst.sink_slots, decodebin_name, (gpointer)sink)) {
+ LOG_ERROR("should not be reached here, bin_name[%s] already exist, sink id[%u] will be removed", decodebin_name, sink->id);
+ g_hash_table_remove(webrtc->gst.sink_slots, decodebin_name);
+ return WEBRTC_ERROR_INVALID_OPERATION;
+ }
+
+ gst_element_sync_state_with_parent(sink->bin);
+
+ LOG_INFO("added a sink slot[%p, id:%u]", sink, sink->id);
+
+ return WEBRTC_ERROR_NONE;
+
+error_before_insert:
+ gst_object_unref(sink_pad);
+ g_free(decodebin_name);
+ g_free(sink);
+
+ return WEBRTC_ERROR_INVALID_OPERATION;
+}
--- /dev/null
+/*
+ * Copyright (c) 2020 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"
+
+#define GST_KLASS_NAME_ENCODER_AUDIO "Codec/Encoder/Audio"
+#define GST_KLASS_NAME_ENCODER_VIDEO "Codec/Encoder/Video"
+#define GST_KLASS_NAME_DECODER_AUDIO "Codec/Decoder/Audio"
+#define GST_KLASS_NAME_DECODER_VIDEO "Codec/Decoder/Video"
+#define GST_KLASS_NAME_PAYLOADER_RTP "Codec/Payloader/Network/RTP"
+#define GST_KLASS_NAME_DEPAYLOADER_RTP "Codec/Depayloader/Network/RTP"
+#define GST_KLASS_NAME_CONVERTER_AUDIO "Filter/Converter/Audio"
+#define GST_KLASS_NAME_CONVERTER_VIDEO "Filter/Converter/Video"
+
+#define DEFAULT_ELEMENT_CAMERASRC "camerasrc"
+#define DEFAULT_ELEMENT_AUDIOSRC "pulsesrc"
+#define DEFAULT_ELEMENT_VIDEOTESTSRC "videotestsrc"
+#define DEFAULT_ELEMENT_AUDIOTESTSRC "audiotestsrc"
+#define DEFAULT_ELEMENT_CAPSFILTER "capsfilter"
+#define DEFAULT_ELEMENT_QUEUE "queue"
+
+#define DEFAULT_VIDEO_ENCODED_MEDIA_TYPE "video/x-vp8"
+#define DEFAULT_VIDEO_RAW_MEDIA_TYPE "video/x-raw"
+#define DEFAULT_VIDEO_RAW_FORMAT "I420"
+#define DEFAULT_VIDEO_WIDTH 352
+#define DEFAULT_VIDEO_HEIGHT 288
+
+#define DEFAULT_AUDIO_ENCODED_MEDIA_TYPE "audio/x-opus"
+#define DEFAULT_AUDIO_RAW_MEDIA_TYPE "audio/x-raw"
+#define DEFAULT_AUDIO_RAW_FORMAT "S16LE"
+#define DEFAULT_AUDIO_CHANNELS 1
+#define DEFAULT_AUDIO_SAMPLERATE 8000
+
+typedef enum {
+ CODEC_TYPE_OPUS,
+ CODEC_TYPE_VORBIS,
+ CODEC_TYPE_VP8,
+ CODEC_TYPE_VP9,
+ CODEC_TYPE_THEORA,
+ CODEC_TYPE_H263,
+ CODEC_TYPE_H264,
+ CODEC_TYPE_H265,
+ CODEC_TYPE_NOT_SUPPORTED,
+} codec_type_e;
+
+typedef struct {
+ const char *encoding_name;
+ const int clock_rate;
+} payload_type_s;
+
+static payload_type_s payload_types[] = {
+ /* AUDIO */
+ [CODEC_TYPE_OPUS] = { "OPUS", 48000 },
+ [CODEC_TYPE_VORBIS] = { "VORBIS", -1 }, /* NOTE: -1 for various clock rate */
+ /* VIDEO */
+ [CODEC_TYPE_VP8] = { "VP8", 90000 },
+ [CODEC_TYPE_VP9] = { "VP9", 90000 },
+ [CODEC_TYPE_THEORA] = { "THEORA", 90000 },
+ [CODEC_TYPE_H263] = { "H263", 90000 },
+ [CODEC_TYPE_H264] = { "H264", 90000 },
+ [CODEC_TYPE_H265] = { "H265", 90000 },
+};
+
+static GstCaps *__make_default_raw_caps(webrtc_media_source_type_e type)
+{
+ GstCaps *caps = NULL;
+
+ switch (type) {
+ case WEBRTC_MEDIA_SOURCE_TYPE_CAMERA:
+ case WEBRTC_MEDIA_SOURCE_TYPE_VIDEOTEST:
+ /* FIXME: get default value from ini */
+ caps = gst_caps_new_simple(DEFAULT_VIDEO_RAW_MEDIA_TYPE,
+ "format", G_TYPE_STRING, DEFAULT_VIDEO_RAW_FORMAT,
+ "width", G_TYPE_INT, DEFAULT_VIDEO_WIDTH,
+ "height", G_TYPE_INT, DEFAULT_VIDEO_HEIGHT,
+ NULL);
+ break;
+
+ case WEBRTC_MEDIA_SOURCE_TYPE_MIC:
+ case WEBRTC_MEDIA_SOURCE_TYPE_AUDIOTEST:
+ /* FIXME: get default value from ini */
+ caps = gst_caps_new_simple(DEFAULT_AUDIO_RAW_MEDIA_TYPE,
+ "format", G_TYPE_STRING, DEFAULT_AUDIO_RAW_FORMAT,
+ "channels", G_TYPE_INT, DEFAULT_AUDIO_CHANNELS,
+ "rate", G_TYPE_INT, DEFAULT_AUDIO_SAMPLERATE,
+ NULL);
+ break;
+
+ default:
+ LOG_ERROR_IF_REACHED("type(%d)", type);
+ break;
+ }
+
+ return caps;
+}
+
+/* Use g_free() to free the media_type parameter. */
+static GstCaps *__make_default_encoded_caps(webrtc_media_source_type_e type, gchar **media_type)
+{
+ GstCaps *caps;
+ const char *_media_type;
+
+ switch (type) {
+ case WEBRTC_MEDIA_SOURCE_TYPE_CAMERA:
+ case WEBRTC_MEDIA_SOURCE_TYPE_VIDEOTEST:
+ /* FIXME: get default value from ini */
+ _media_type = DEFAULT_VIDEO_ENCODED_MEDIA_TYPE;
+ caps = gst_caps_new_simple(_media_type,
+ "width", G_TYPE_INT, DEFAULT_VIDEO_WIDTH,
+ "height", G_TYPE_INT, DEFAULT_VIDEO_HEIGHT,
+ NULL);
+ break;
+
+ case WEBRTC_MEDIA_SOURCE_TYPE_MIC:
+ case WEBRTC_MEDIA_SOURCE_TYPE_AUDIOTEST:
+ /* FIXME: get default value from ini */
+ _media_type = DEFAULT_AUDIO_ENCODED_MEDIA_TYPE;
+ caps = gst_caps_new_simple(DEFAULT_AUDIO_ENCODED_MEDIA_TYPE,
+ "channels", G_TYPE_INT, DEFAULT_AUDIO_CHANNELS,
+ "rate", G_TYPE_INT, DEFAULT_AUDIO_SAMPLERATE,
+ NULL);
+ break;
+
+ default:
+ LOG_ERROR_IF_REACHED("type(%d)", type);
+ return NULL;
+ }
+
+ if (media_type)
+ *media_type = g_strdup(_media_type);
+
+ return caps;
+}
+
+static codec_type_e __get_codec_type(const gchar *media_type)
+{
+ if (!g_strcmp0(media_type, "audio/x-opus"))
+ return CODEC_TYPE_OPUS;
+ else if (!g_strcmp0(media_type, "audio/x-vorbis"))
+ return CODEC_TYPE_VORBIS;
+ else if (!g_strcmp0(media_type, "video/x-vp8"))
+ return CODEC_TYPE_VP8;
+ else if (!g_strcmp0(media_type, "video/x-vp9"))
+ return CODEC_TYPE_VP9;
+ else if (!g_strcmp0(media_type, "video/x-theora"))
+ return CODEC_TYPE_THEORA;
+ else if (!g_strcmp0(media_type, "video/x-h263"))
+ return CODEC_TYPE_H263;
+ else if (!g_strcmp0(media_type, "video/x-h264"))
+ return CODEC_TYPE_H264;
+ else if (!g_strcmp0(media_type, "video/x-h265"))
+ return CODEC_TYPE_H265;
+ else
+ return CODEC_TYPE_NOT_SUPPORTED;
+}
+
+static GstCaps *__make_rtp_caps(const gchar *media_type, unsigned int id)
+{
+ gchar *caps_str;
+ GstCaps *caps;
+ codec_type_e codec_type = __get_codec_type(media_type);
+
+ RET_VAL_IF(codec_type == CODEC_TYPE_NOT_SUPPORTED, NULL, "media_type[%s] is not supported", media_type);
+
+ caps = gst_caps_new_simple("application/x-rtp",
+ "media", G_TYPE_STRING, g_strrstr(media_type, "video") ? "video" : "audio",
+ "clock-rate", G_TYPE_INT, payload_types[codec_type].clock_rate, /* FIXME: support various clock-rate */
+ "encoding-name", G_TYPE_STRING, payload_types[codec_type].encoding_name,
+ "payload", G_TYPE_INT, id + 95, NULL);
+
+ caps_str = gst_caps_to_string(caps);
+ LOG_DEBUG("RTP caps is created [%s]", caps_str);
+
+ g_free(caps_str);
+
+ return caps;
+}
+
+static int __create_rest_of_elements(webrtc_gst_slot_s *source, webrtc_media_source_type_e type, GstElement **capsfilter, GstElement **encoder, GstElement **payloader, GstElement **queue, GstElement **capsfilter2)
+{
+ GstCaps *sink_caps;
+ element_info_s elem_info;
+ const gchar *encoder_klass_name;
+ gchar *media_type;
+
+ RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL");
+ RET_VAL_IF(capsfilter == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "capsfilter is NULL");
+ RET_VAL_IF(encoder == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "encoder is NULL");
+ RET_VAL_IF(payloader == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "payloader is NULL");
+ RET_VAL_IF(queue == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "queue is NULL");
+ RET_VAL_IF(capsfilter2 == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "capsfilter2 is NULL");
+
+ if (!(*capsfilter = _create_element(DEFAULT_ELEMENT_CAPSFILTER, NULL))) {
+ LOG_ERROR("failed to create capsfilter");
+ return WEBRTC_ERROR_INVALID_OPERATION;
+ }
+ if ((sink_caps = __make_default_raw_caps(type))) {
+ g_object_set(G_OBJECT(*capsfilter), "caps", sink_caps, NULL);
+ gst_caps_unref(sink_caps);
+ }
+
+ if (type == WEBRTC_MEDIA_SOURCE_TYPE_CAMERA || type == WEBRTC_MEDIA_SOURCE_TYPE_VIDEOTEST)
+ encoder_klass_name = GST_KLASS_NAME_ENCODER_VIDEO;
+ else
+ encoder_klass_name = GST_KLASS_NAME_ENCODER_AUDIO;
+
+ CREATE_ELEMENT_FROM_REGISTRY(elem_info, encoder_klass_name,
+ __make_default_raw_caps(type),
+ __make_default_encoded_caps(type, NULL),
+ *encoder);
+
+ CREATE_ELEMENT_FROM_REGISTRY(elem_info, GST_KLASS_NAME_PAYLOADER_RTP,
+ __make_default_encoded_caps(type, &media_type),
+ NULL,
+ *payloader);
+
+ if (!(*queue = _create_element(DEFAULT_ELEMENT_QUEUE, NULL))) {
+ LOG_ERROR("failed to create queue");
+ g_free(media_type);
+ return WEBRTC_ERROR_INVALID_OPERATION;
+ }
+
+ if (!(*capsfilter2 = _create_element(DEFAULT_ELEMENT_CAPSFILTER, NULL))) {
+ LOG_ERROR("failed to create capsfilter");
+ g_free(media_type);
+ return WEBRTC_ERROR_INVALID_OPERATION;
+ }
+ if ((sink_caps = __make_rtp_caps(media_type, source->id))) {
+ g_object_set(G_OBJECT(*capsfilter2), "caps", sink_caps, NULL);
+ gst_caps_unref(sink_caps);
+ }
+
+ g_free(media_type);
+
+ return WEBRTC_ERROR_NONE;
+}
+
+static int __build_camerasrc(webrtc_gst_slot_s *source, GstPad *ghost_src_pad)
+{
+ int ret = WEBRTC_ERROR_NONE;
+ GstElement *camerasrc;
+ GstElement *capsfilter;
+ GstElement *videoenc;
+ GstElement *videopay;
+ GstElement *queue;
+ GstElement *capsfilter2;
+
+ RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL");
+ RET_VAL_IF(ghost_src_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "ghost_src_pad is NULL");
+ RET_VAL_IF(source->bin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "bin is NULL");
+
+ source->media_types |= MEDIA_TYPE_VIDEO;
+
+ /* FIXME: get factory name from ini */
+ if (!(camerasrc = _create_element(DEFAULT_ELEMENT_CAMERASRC, NULL))) {
+ LOG_ERROR("failed to create camerasrc");
+ return WEBRTC_ERROR_INVALID_OPERATION;
+ }
+ /* FIXME: set camera default setting from ini */
+
+ if ((ret = __create_rest_of_elements(source, WEBRTC_MEDIA_SOURCE_TYPE_CAMERA, &capsfilter, &videoenc, &videopay, &queue, &capsfilter2)) != WEBRTC_ERROR_NONE)
+ return ret;
+
+ gst_bin_add_many(GST_BIN(source->bin), camerasrc, capsfilter, videoenc, videopay, queue, capsfilter2, NULL);
+ if (!gst_element_link_many(camerasrc, capsfilter, videoenc, videopay, queue, capsfilter2, NULL)) {
+ LOG_ERROR("failed to gst_element_link_many()");
+ return WEBRTC_ERROR_INVALID_OPERATION;
+ }
+
+ return _set_ghost_pad_target(ghost_src_pad, capsfilter2, TRUE);
+}
+
+static int __build_audiosrc(webrtc_gst_slot_s *source, GstPad *ghost_src_pad)
+{
+ int ret = WEBRTC_ERROR_NONE;
+ GstElement *audiosrc;
+ GstElement *capsfilter;
+ GstElement *audioenc;
+ GstElement *audiopay;
+ GstElement *queue;
+ GstElement *capsfilter2;
+
+ RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL");
+ RET_VAL_IF(ghost_src_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "ghost_src_pad is NULL");
+ RET_VAL_IF(source->bin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "bin is NULL");
+
+ source->media_types |= MEDIA_TYPE_AUDIO;
+
+ if (!(audiosrc = _create_element(DEFAULT_ELEMENT_AUDIOSRC, NULL))) {
+ LOG_ERROR("failed to create audiosrc");
+ return WEBRTC_ERROR_INVALID_OPERATION;
+ }
+
+ if ((ret = __create_rest_of_elements(source, WEBRTC_MEDIA_SOURCE_TYPE_MIC, &capsfilter, &audioenc, &audiopay, &queue, &capsfilter2)) != WEBRTC_ERROR_NONE)
+ return ret;
+
+ gst_bin_add_many(GST_BIN(source->bin), audiosrc, capsfilter, audioenc, audiopay, queue, capsfilter2, NULL);
+ if (!gst_element_link_many(audiosrc, capsfilter, audioenc, audiopay, queue, capsfilter2, NULL)) {
+ LOG_ERROR("failed to gst_element_link_many()");
+ return WEBRTC_ERROR_INVALID_OPERATION;
+ }
+
+ return _set_ghost_pad_target(ghost_src_pad, capsfilter2, TRUE);
+}
+
+static int __build_videotestsrc(webrtc_gst_slot_s *source, GstPad *ghost_src_pad)
+{
+ int ret = WEBRTC_ERROR_NONE;
+ GstElement *videotestsrc;
+ GstElement *capsfilter;
+ GstElement *videoenc;
+ GstElement *videopay;
+ GstElement *queue;
+ GstElement *capsfilter2;
+
+ RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL");
+ RET_VAL_IF(ghost_src_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "ghost_src_pad is NULL");
+ RET_VAL_IF(source->bin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "bin is NULL");
+
+ source->media_types |= MEDIA_TYPE_VIDEO;
+
+ if (!(videotestsrc = _create_element(DEFAULT_ELEMENT_VIDEOTESTSRC, NULL))) {
+ LOG_ERROR("failed to create videotestsrc");
+ return WEBRTC_ERROR_INVALID_OPERATION;
+ }
+ g_object_set(G_OBJECT(videotestsrc), "is-live", TRUE, NULL);
+
+ if ((ret = __create_rest_of_elements(source, WEBRTC_MEDIA_SOURCE_TYPE_VIDEOTEST, &capsfilter, &videoenc, &videopay, &queue, &capsfilter2)) != WEBRTC_ERROR_NONE)
+ return ret;
+
+ gst_bin_add_many(GST_BIN(source->bin), videotestsrc, capsfilter, videoenc, videopay, queue, capsfilter2, NULL);
+ if (!gst_element_link_many(videotestsrc, capsfilter, videoenc, videopay, queue, capsfilter2, NULL)) {
+ LOG_ERROR("failed to gst_element_link_many()");
+ return WEBRTC_ERROR_INVALID_OPERATION;
+ }
+
+ return _set_ghost_pad_target(ghost_src_pad, capsfilter2, TRUE);
+}
+
+static int __build_audiotestsrc(webrtc_gst_slot_s *source, GstPad *ghost_src_pad)
+{
+ int ret = WEBRTC_ERROR_NONE;
+ GstElement *audiotestsrc;
+ GstElement *capsfilter;
+ GstElement *audioenc;
+ GstElement *audiopay;
+ GstElement *queue;
+ GstElement *capsfilter2;
+
+ RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL");
+ RET_VAL_IF(ghost_src_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "ghost_src_pad is NULL");
+ RET_VAL_IF(source->bin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "bin is NULL");
+
+ source->media_types |= MEDIA_TYPE_AUDIO;
+
+ if (!(audiotestsrc = _create_element(DEFAULT_ELEMENT_AUDIOTESTSRC, NULL))) {
+ LOG_ERROR("failed to create audiotestsrc");
+ return WEBRTC_ERROR_INVALID_OPERATION;
+ }
+ g_object_set(G_OBJECT(audiotestsrc), "is-live", TRUE, NULL);
+
+ if ((ret = __create_rest_of_elements(source, WEBRTC_MEDIA_SOURCE_TYPE_AUDIOTEST, &capsfilter, &audioenc, &audiopay, &queue, &capsfilter2)) != WEBRTC_ERROR_NONE)
+ return ret;
+
+ gst_bin_add_many(GST_BIN(source->bin), audiotestsrc, capsfilter, audioenc, audiopay, queue, capsfilter2, NULL);
+ if (!gst_element_link_many(audiotestsrc, capsfilter, audioenc, audiopay, queue, capsfilter2, NULL)) {
+ LOG_ERROR("failed to gst_element_link_many()");
+ return WEBRTC_ERROR_INVALID_OPERATION;
+ }
+
+ return _set_ghost_pad_target(ghost_src_pad, capsfilter2, TRUE);
+}
+
+static int __build_source_bin(webrtc_gst_slot_s *source, webrtc_media_source_type_e type)
+{
+ int ret = WEBRTC_ERROR_NONE;
+ GstPad *src_pad;
+
+ 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");
+
+ 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()");
+
+ switch (type) {
+ case WEBRTC_MEDIA_SOURCE_TYPE_CAMERA:
+ return __build_camerasrc(source, src_pad);
+
+ case WEBRTC_MEDIA_SOURCE_TYPE_MIC:
+ return __build_audiosrc(source, src_pad);
+
+ case WEBRTC_MEDIA_SOURCE_TYPE_VIDEOTEST:
+ return __build_videotestsrc(source, src_pad);
+
+ case WEBRTC_MEDIA_SOURCE_TYPE_AUDIOTEST:
+ return __build_audiotestsrc(source, src_pad);
+
+ default:
+ LOG_ERROR_IF_REACHED("type(%d)", type);
+ return WEBRTC_ERROR_INVALID_PARAMETER;
+ }
+
+ return WEBRTC_ERROR_NONE;
+}
+
+static unsigned int __get_unoccupied_id(GHashTable *slots)
+{
+ int i;
+ gchar *key;
+
+ RET_VAL_IF(slots == NULL, 0, "slot is NULL");
+
+ /* Payload identifiers 96–127 are used for payloads defined dynamically during a session,
+ * hence the id range is limited here to 1-32. */
+ for (i = 1; i < 33; i++) {
+ key = g_strdup_printf("media_source_%u", i);
+ if (g_hash_table_contains(slots, key)) {
+ g_free(key);
+ continue;
+ }
+ g_free(key);
+ return i;
+ }
+
+ LOG_ERROR("all slots are occupied(1-32)");
+
+ return 0;
+}
+
+int _add_media_source(webrtc_s *webrtc, webrtc_media_source_type_e type, unsigned int *source_id)
+{
+ int ret = WEBRTC_ERROR_NONE;
+ unsigned int id;
+ webrtc_gst_slot_s *source = NULL;
+ gchar *bin_name = NULL;
+ GstPad *webrtc_sinkpad;
+ gchar *webrtc_sinkpad_name;
+ gchar *bin_srcpad_name = NULL;
+
+ RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
+ RET_VAL_IF(webrtc->gst.source_slots == NULL, WEBRTC_ERROR_INVALID_OPERATION, "source_slots is NULL");
+ RET_VAL_IF(source_id == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source_id is NULL");
+
+ /* bin_name/source will be freed by function which is set to g_hash_table_new_full() */
+ id = __get_unoccupied_id(webrtc->gst.source_slots);
+ RET_VAL_IF(id == 0, WEBRTC_ERROR_INVALID_OPERATION, "source_slots are full");
+
+ bin_name = g_strdup_printf("media_source_%u", id);
+
+ MALLOC_AND_INIT_SLOT(source, id, bin_name);
+
+ ret = __build_source_bin(source, type);
+ if (ret != WEBRTC_ERROR_NONE) {
+ LOG_ERROR("failed to __build_source_bin()");
+ goto error;
+ }
+
+ if (!gst_bin_add(GST_BIN(webrtc->gst.pipeline), source->bin)) {
+ LOG_ERROR("failed to gst_bin_add(), [%s] -> [%s] pipeline", GST_ELEMENT_NAME(source->bin), GST_ELEMENT_NAME(webrtc->gst.pipeline));
+ goto error;
+ }
+
+ /* The gst_element_get_request_pad() of webrtcbin will trigger the transciever callback. To update the mline value of
+ * new transceiver object to the source structure in the callback, hash table inserting should be preceded. */
+ if (!g_hash_table_insert(webrtc->gst.source_slots, bin_name, (gpointer)source)) {
+ LOG_ERROR("should not be reached here, bin_name[%s] already exist, source id[%u] will be removed", bin_name, source->id);
+ g_hash_table_remove(webrtc->gst.source_slots, bin_name);
+ return WEBRTC_ERROR_INVALID_OPERATION;
+ }
+
+ if (!(webrtc_sinkpad = gst_element_get_request_pad(webrtc->gst.webrtcbin, "sink_%u"))) {
+ LOG_ERROR("failed to gst_element_get_request_pad()");
+ goto error_after_insert;
+ }
+ if (!(webrtc_sinkpad_name = gst_pad_get_name(webrtc_sinkpad))) {
+ LOG_ERROR("failed to gst_pad_get_name()");
+ goto error_after_insert;
+ }
+ bin_srcpad_name = g_strdup_printf("src_%u", id);
+ if (!gst_element_link_pads(source->bin, bin_srcpad_name, webrtc->gst.webrtcbin, webrtc_sinkpad_name)) {
+ LOG_ERROR("failed to link pads, [%s:%s] - [%s:%s]",
+ GST_ELEMENT_NAME(source->bin), bin_srcpad_name, GST_ELEMENT_NAME(webrtc->gst.webrtcbin), webrtc_sinkpad_name);
+ goto error_after_insert;
+ }
+ LOG_DEBUG("link pads successfully, [%s:%s] - [%s:%s]",
+ GST_ELEMENT_NAME(source->bin), bin_srcpad_name, GST_ELEMENT_NAME(webrtc->gst.webrtcbin), webrtc_sinkpad_name);
+
+ *source_id = id;
+
+ LOG_INFO("added a source slot[%p, id:%u]", source, source->id);
+
+ g_free(bin_srcpad_name);
+
+ return WEBRTC_ERROR_NONE;
+
+error_after_insert:
+ g_hash_table_remove(webrtc->gst.source_slots, bin_name);
+ g_free(bin_srcpad_name);
+
+ return WEBRTC_ERROR_INVALID_OPERATION;
+
+error:
+ g_free(bin_name);
+ g_free(source);
+
+ return WEBRTC_ERROR_INVALID_OPERATION;
+}
+
+int _remove_media_source(webrtc_s *webrtc, unsigned int source_id)
+{
+ int ret = WEBRTC_ERROR_NONE;
+ gchar *bin_name = NULL;
+
+ RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
+ RET_VAL_IF(webrtc->gst.source_slots == NULL, WEBRTC_ERROR_INVALID_OPERATION, "source_slots is NULL");
+
+ bin_name = g_strdup_printf("media_source_%u", source_id);
+
+ if (!g_hash_table_remove(webrtc->gst.source_slots, (gpointer)bin_name)) {
+ LOG_ERROR("failed to find media source by id[%u]", source_id);
+ ret = WEBRTC_ERROR_INVALID_PARAMETER;
+ }
+
+ g_free(bin_name);
+
+ return ret;
+}