#define MEDIA_STREAMER_CONVERTER_KLASS "Filter/Converter"
#define MEDIA_STREAMER_DECODER_KLASS "Codec/Decoder"
#define MEDIA_STREAMER_SINK_KLASS "Sink"
+#define MEDIA_STREAMER_STRICT "_strict_"
+
/**
* @brief Generates dot files for GStreamer pipeline.
*/
GstElement *__ms_element_create(const char *plugin_name, const char *name);
+/**
+ * @brief Creates GstElement from specified node_plug_s structure.
+ *
+ * @since_tizen 3.0
+ */
+GstElement *__ms_node_element_create(node_plug_s *plug_info, media_streamer_node_type_e type);
+
/**
* @brief Creates encoder GstElement by mime type.
*
*
* @since_tizen 3.0
*/
-GstElement *__ms_rtp_element_create(media_streamer_node_s *ms_node);
+GstElement *__ms_rtp_element_create(void);
/**
* @brief Prepares rtp container according to parameters data.
*/
int __ms_element_set_fmt(media_streamer_node_s *node, const char *pad_name, media_format_h fmt);
+/**
+ * @brief Creates GstCap's from mediaformat.
+ *
+ * @since_tizen 3.0
+ */
+GstCaps *__ms_create_caps_from_fmt(media_format_h fmt);
/**
* @brief Seeks GstElement to according time value.
*
char *origin_name;
} param_s;
+/**
+ * @brief Media Streamer node_info type handle.
+ *
+ * @since_tizen 3.0
+ */
+typedef struct {
+ char *klass_name;
+ char *default_name;
+} node_info_s;
+
+/**
+ * @brief Media Streamer node_plug type handle.
+ *
+ * @since_tizen 3.0
+ */
+typedef struct {
+ node_info_s *info;
+ GstCaps *src_caps;
+ GstCaps *sink_caps;
+ gchar **exclude_names;
+} node_plug_s;
+
/**
* @brief Media Streamer type handle.
*
/* Ini Utils */
#ifndef MEDIA_STREAMER_INI_PATH
- #define MEDIA_STREAMER_INI_PATH "/etc/media_streamer.ini"
+ #define MEDIA_STREAMER_INI_PATH "/etc/media_streamer.ini"
#endif
-#define MEDIA_STREAMER_INI_MAX_STRLEN 100
+#define MEDIA_STREAMER_INI_MAX_STRLEN 100
#define RTP_STREAM_DISABLED (0)
/**
g_object_set_data_full(G_OBJECT(obj), key, (gpointer)val, __ms_rtp_param_value_destroy); \
} while (0)
+#define MS_GET_CAPS_TYPE(caps, type) \
+ do { \
+ if (caps && gst_caps_get_size(caps) > 0) \
+ type = gst_structure_get_name(gst_caps_get_structure(caps, 0)); \
+ else \
+ type = NULL; \
+ } while (0)
+
/**
* @brief Loads media streamer settings from ini file.
* The default values will be used if error has occurred.
*/
gchar *__ms_ini_get_string(dictionary *dict, const char *ini_path, char *default_str);
+/**
+ * @brief Reads comma-separated string list from ini file.
+ *
+ * @since_tizen 3.0
+ */
+void __ms_ini_read_list(dictionary *dict, const char *key, gchar ***list);
+
/**
* @brief Converts Media Format mime type into Caps media format string.
*
__ms_node_destroy(ms_node);
ms_error("Error creating Node [%d]", ret);
return ret;
+ } else {
+ ms_info("Node [%s] created", ms_node->name);
}
- ms_info("Node [%s] created", ms_node->name);
*node = (media_streamer_node_h) ms_node;
return ret;
#define H264_PARSER_CONFIG_INTERVAL 5
#define H264_ENCODER_ZEROLATENCY 0x00000004
+#define NODE_CONF_FIELD_LEN 100
+
void __ms_generate_dots(GstElement *bin, gchar *name_tag)
{
gchar *dot_name;
const gchar *__ms_get_pad_type(GstPad *element_pad)
{
- const gchar *element_pad_type = NULL;
- GstCaps *element_pad_caps = gst_pad_query_caps(element_pad, 0);
- if (gst_caps_get_size(element_pad_caps) > 0) {
- GstStructure *element_pad_struct = gst_caps_get_structure(element_pad_caps, 0);
- element_pad_type = gst_structure_get_name(element_pad_struct);
- } else {
- ms_debug("Caps size of element is 0");
- }
- gst_caps_unref(element_pad_caps);
- return element_pad_type;
+ const gchar *pad_type = NULL;
+ GstCaps *pad_caps = gst_pad_query_caps(element_pad, 0);
+ MS_GET_CAPS_TYPE(pad_caps, pad_type);
+ gst_caps_unref(pad_caps);
+ return pad_type;
}
static gboolean __ms_intersect_pads(GstPad *src_pad, GstPad *sink_pad)
previous_element = __ms_combine_next_element(previous_element, NULL, ms_streamer->sink_bin, MEDIA_STREAMER_SINK_KLASS, NULL, NULL);
}
} else if (MS_ELEMENT_IS_AUDIO(src_pad_type)) {
- previous_element = __ms_combine_next_element(previous_element, NULL, ms_streamer->topology_bin, MEDIA_STREAMER_DECODER_KLASS, NULL, NULL);
+ previous_element = __ms_combine_next_element(previous_element, src_pad, ms_streamer->topology_bin, MEDIA_STREAMER_DECODER_KLASS, NULL, NULL);
previous_element = __ms_combine_next_element(previous_element, NULL, ms_streamer->sink_bin, MEDIA_STREAMER_QUEUE_KLASS, NULL, DEFAULT_QUEUE);
previous_element = __ms_combine_next_element(previous_element, NULL, ms_streamer->sink_bin, MEDIA_STREAMER_SINK_KLASS, NULL, NULL);
} else {
return plugin_elem;
}
+static gboolean __ms_feature_node_filter(GstPluginFeature *feature, gpointer data)
+{
+ node_plug_s *plug_info = (node_plug_s*)data;
+
+ if (!GST_IS_ELEMENT_FACTORY(feature))
+ return FALSE;
+
+ gboolean can_accept = FALSE;
+ GstElementFactory *factory = GST_ELEMENT_FACTORY(feature);
+ const gchar *factory_klass = gst_element_factory_get_klass(factory);
+
+ if (plug_info && g_strrstr(factory_klass, plug_info->info->klass_name)) {
+
+ if (GST_IS_CAPS(plug_info->src_caps))
+ can_accept = can_accept || gst_element_factory_can_src_any_caps(factory, plug_info->src_caps);
+
+ if (GST_IS_CAPS(plug_info->sink_caps))
+ can_accept = can_accept || gst_element_factory_can_sink_any_caps(factory, plug_info->sink_caps);
+
+ if (can_accept) {
+ int index = 0;
+ for ( ; plug_info->exclude_names && plug_info->exclude_names[index]; ++index) {
+ if (g_strrstr(GST_OBJECT_NAME(factory), plug_info->exclude_names[index])) {
+ ms_debug("Skipping compatible factory [%s] as excluded.", GST_OBJECT_NAME(factory));
+ return FALSE;
+ }
+ }
+
+ ms_info("[INFO] Found compatible factory [%s] for klass [%s]",
+ GST_OBJECT_NAME(factory), factory_klass);
+ return TRUE;
+ }
+
+ }
+
+ return FALSE;
+}
+
+GstElement *__ms_node_element_create(node_plug_s *plug_info, media_streamer_node_type_e type)
+{
+ dictionary *dict = NULL;
+ GstElement *gst_element = NULL;
+
+ const gchar *src_type, *sink_type;
+ MS_GET_CAPS_TYPE(plug_info->src_caps, src_type);
+ MS_GET_CAPS_TYPE(plug_info->sink_caps, sink_type);
+
+ /* 1. Main priority:
+ * If Node klass defined as MEDIA_STREAMER_STRICT,
+ * element will be created immediately by default_name */
+ if (!strncmp(plug_info->info->klass_name, MEDIA_STREAMER_STRICT, 10)
+ || ( !src_type && !sink_type)) {
+
+ if(type == MEDIA_STREAMER_NODE_TYPE_RTP)
+ gst_element = __ms_rtp_element_create();
+ else
+ gst_element = __ms_element_create(plug_info->info->default_name, NULL);
+ } else {
+ /* 2. Second priority:
+ * Try to get plugin name that defined in ini file
+ * according with node type and specified format. */
+ ms_info("Specified node formats types: in[%s] - out[%s]", sink_type, src_type);
+ gchar conf_key[NODE_CONF_FIELD_LEN] = {0,};
+ if (snprintf(conf_key, NODE_CONF_FIELD_LEN, "node type %d:%s", type, (sink_type ? sink_type : src_type)) >= NODE_CONF_FIELD_LEN) {
+ ms_error("Failed to generate config field name, size >= %d", NODE_CONF_FIELD_LEN);
+ return NULL;
+ }
+
+ __ms_load_ini_dictionary(&dict);
+ gchar *plugin_name = __ms_ini_get_string(dict, conf_key, NULL);
+
+ if (plugin_name) {
+ gst_element = __ms_element_create(plugin_name, NULL);
+ MS_SAFE_GFREE(plugin_name);
+ }
+ __ms_destroy_ini_dictionary(dict);
+ }
+
+ /* 3. Third priority:
+ * If previous cases did not create a valid gst_element,
+ * try to find compatible plugin in gstreamer registry.
+ * Elements that are compatible but defined as excluded will be skipped*/
+ if(!gst_element) {
+ __ms_load_ini_dictionary(&dict);
+
+ /* Read exclude elements list */
+ __ms_ini_read_list(dict, "general:exclude elements", &plug_info->exclude_names);
+
+ GList *factories = gst_registry_feature_filter(gst_registry_get(),
+ __ms_feature_node_filter, TRUE, plug_info);
+
+ if (factories) {
+ GstElementFactory *factory = GST_ELEMENT_FACTORY(factories->data);
+ gst_element = __ms_element_create(GST_OBJECT_NAME(factory), NULL);
+ } else {
+ ms_error("Error: could not found any compatible element for node [%d]: in[%s] - out[%s]",
+ type, sink_type, src_type);
+ }
+
+ g_strfreev(plug_info->exclude_names);
+ gst_plugin_list_free(factories);
+ __ms_destroy_ini_dictionary(dict);
+ }
+
+ return gst_element;
+}
+
GstElement *__ms_video_encoder_element_create(dictionary * dict, media_format_mimetype_e mime)
{
char *plugin_name = NULL;
return audio_enc_bin;
}
-GstElement *__ms_rtp_element_create(media_streamer_node_s * ms_node)
+GstElement *__ms_rtp_element_create(void)
{
- ms_retvm_if(ms_node == NULL, (GstElement *) NULL, "Error empty rtp node Handle");
-
GstElement *rtp_container = gst_bin_new("rtp_container");
ms_retvm_if(!rtp_container, (GstElement *) NULL, "Error: creating elements for rtp container");
return ret;
}
-static GstCaps *__ms_create_caps_from_fmt(media_format_h fmt)
+GstCaps *__ms_create_caps_from_fmt(media_format_h fmt)
{
GstCaps *caps = NULL;
gchar *caps_name = NULL;
{NULL, NULL}
};
+node_info_s nodes_info[] = {
+ {NULL, NULL}, /* MEDIA_STREAMER_NODE_TYPE_NONE */
+ {"Source", "fakesrc"}, /* MEDIA_STREAMER_NODE_TYPE_SRC */
+ {"Sink", "fakesink"}, /* MEDIA_STREAMER_NODE_TYPE_SINK */
+ {"Codec/Encoder", "x264enc"}, /* MEDIA_STREAMER_NODE_TYPE_VIDEO_ENCODER */
+ {"Codec/Decoder", "avdec_h264"}, /* MEDIA_STREAMER_NODE_TYPE_VIDEO_DECODER */
+ {"Codec/Encoder", "amrnbenc"}, /* MEDIA_STREAMER_NODE_TYPE_AUDIO_ENCODER */
+ {"Codec/Decoder", "amrnbdec"}, /* MEDIA_STREAMER_NODE_TYPE_AUDIO_DECODER */
+ {MEDIA_STREAMER_STRICT, "videoconvert"}, /* MEDIA_STREAMER_NODE_TYPE_VIDEO_CONVERTER */
+ {MEDIA_STREAMER_STRICT, "audioconvert"}, /* MEDIA_STREAMER_NODE_TYPE_AUDIO_CONVERTER */
+ {MEDIA_STREAMER_STRICT, "audioresample"}, /* MEDIA_STREAMER_NODE_TYPE_AUDIO_RESAMPLE */
+ {"Payloader", "rtph264pay"}, /* MEDIA_STREAMER_NODE_TYPE_VIDEO_PAY */
+ {"Payloader", "rtpamrpay"}, /* MEDIA_STREAMER_NODE_TYPE_AUDIO_PAY */
+ {"Depayloader", "rtph264depay"}, /* MEDIA_STREAMER_NODE_TYPE_VIDEO_DEPAY */
+ {"Depayloader", "rtpamrdepay"}, /* MEDIA_STREAMER_NODE_TYPE_AUDIO_DEPAY */
+ {MEDIA_STREAMER_STRICT, "capsfilter"}, /* MEDIA_STREAMER_NODE_TYPE_FILTER */
+ {MEDIA_STREAMER_STRICT, "tee"}, /* MEDIA_STREAMER_NODE_TYPE_TEE */
+ {MEDIA_STREAMER_STRICT, "queue"}, /* MEDIA_STREAMER_NODE_TYPE_QUEUE */
+ {MEDIA_STREAMER_STRICT, "multiqueue"}, /* MEDIA_STREAMER_NODE_TYPE_MQUEUE */
+ {"Codec/Muxer", "qtmux"}, /* MEDIA_STREAMER_NODE_TYPE_MUXER */
+ {"Codec/Demuxer", "qtdemux"}, /* MEDIA_STREAMER_NODE_TYPE_DEMUXER */
+ {MEDIA_STREAMER_STRICT, "rtpbin"}, /* MEDIA_STREAMER_NODE_TYPE_RTP */
+ {MEDIA_STREAMER_STRICT, "input-selector"}, /* MEDIA_STREAMER_NODE_TYPE_INPUT_SELECTOR */
+ {MEDIA_STREAMER_STRICT, "output-selector"},/* MEDIA_STREAMER_NODE_TYPE_OUTPUT_SELECTOR */
+ {MEDIA_STREAMER_STRICT, "interleave"}, /* MEDIA_STREAMER_NODE_TYPE_INTERLEAVE */
+ {MEDIA_STREAMER_STRICT, "deinterleave"}, /* MEDIA_STREAMER_NODE_TYPE_DEINTERLEAVE */
+ {NULL, NULL}
+};
+
static gboolean __ms_rtp_node_has_property(media_streamer_node_s *ms_node, const gchar *param_name)
{
ms_retvm_if(!ms_node || !ms_node->gst_element, FALSE, "Error: empty node");
ms_retvm_if(node == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "Handle is NULL");
int ret = MEDIA_STREAMER_ERROR_NONE;
- dictionary *dict = NULL;
- char *plugin_name = NULL;
- media_format_mimetype_e mime;
- if (MEDIA_FORMAT_ERROR_NONE != media_format_get_video_info(out_fmt, &mime, NULL, NULL, NULL, NULL))
- media_format_get_audio_info(out_fmt, &mime, NULL, NULL, NULL, NULL);
- char *format_prefix = NULL;
+ GstCaps *sink_caps = in_fmt ? __ms_create_caps_from_fmt(in_fmt) : NULL;
+ GstCaps *src_caps = out_fmt ? __ms_create_caps_from_fmt(out_fmt) : NULL;
- __ms_load_ini_dictionary(&dict);
+ node_plug_s plug_info = {&(nodes_info[node->type]), src_caps, sink_caps, NULL};
+ ms_info("Creating node with info: klass_name[%s]; default[%s]",
+ plug_info.info->klass_name, plug_info.info->default_name);
- switch (node->type) {
- case MEDIA_STREAMER_NODE_TYPE_VIDEO_ENCODER:
- format_prefix = g_strdup_printf("%s:encoder", __ms_convert_mime_to_string(mime));
- plugin_name = __ms_ini_get_string(dict, format_prefix, DEFAULT_VIDEO_ENCODER);
- node->gst_element = __ms_video_encoder_element_create(dict, mime);
- break;
- case MEDIA_STREAMER_NODE_TYPE_VIDEO_DECODER:
- format_prefix = g_strdup_printf("%s:decoder", __ms_convert_mime_to_string(mime));
- plugin_name = __ms_ini_get_string(dict, format_prefix, DEFAULT_VIDEO_DECODER);
- node->gst_element = __ms_video_decoder_element_create(dict, mime);
- break;
- case MEDIA_STREAMER_NODE_TYPE_PARSER:
- format_prefix = g_strdup_printf("%s:parser", __ms_convert_mime_to_string(mime));
- plugin_name = __ms_ini_get_string(dict, format_prefix, DEFAULT_VIDEO_PARSER);
- node->gst_element = __ms_element_create(plugin_name, NULL);
- break;
- case MEDIA_STREAMER_NODE_TYPE_FILTER:
- node->gst_element = __ms_element_create(DEFAULT_FILTER, NULL);
- break;
- case MEDIA_STREAMER_NODE_TYPE_VIDEO_PAY:
- format_prefix = g_strdup_printf("%s:rtppay", __ms_convert_mime_to_string(mime));
- plugin_name = __ms_ini_get_string(dict, format_prefix, DEFAULT_VIDEO_RTPPAY);
- node->gst_element = __ms_element_create(plugin_name, NULL);
- break;
- case MEDIA_STREAMER_NODE_TYPE_AUDIO_PAY:
- plugin_name = __ms_ini_get_string(dict, "audio-raw:rtppay", DEFAULT_AUDIO_RTPPAY);
- node->gst_element = __ms_element_create(plugin_name, NULL);
- break;
- case MEDIA_STREAMER_NODE_TYPE_VIDEO_DEPAY:
- format_prefix = g_strdup_printf("%s:rtpdepay", __ms_convert_mime_to_string(mime));
- plugin_name = __ms_ini_get_string(dict, format_prefix, DEFAULT_VIDEO_RTPDEPAY);
- node->gst_element = __ms_element_create(plugin_name, NULL);
- break;
- case MEDIA_STREAMER_NODE_TYPE_AUDIO_DEPAY:
- plugin_name = __ms_ini_get_string(dict, "audio-raw:rtpdepay", DEFAULT_AUDIO_RTPDEPAY);
- node->gst_element = __ms_element_create(plugin_name, NULL);
- break;
- case MEDIA_STREAMER_NODE_TYPE_RTP:
- node->gst_element = __ms_rtp_element_create(node);
- break;
- case MEDIA_STREAMER_NODE_TYPE_QUEUE:
- node->gst_element = __ms_element_create(DEFAULT_QUEUE, NULL);
- break;
- case MEDIA_STREAMER_NODE_TYPE_AUDIO_ENCODER:
- node->gst_element = __ms_audio_encoder_element_create();
- break;
- case MEDIA_STREAMER_NODE_TYPE_AUDIO_DECODER:
- node->gst_element = __ms_element_create(DEFAULT_AUDIO_DECODER, NULL);
- break;
- case MEDIA_STREAMER_NODE_TYPE_VIDEO_CONVERTER:
- node->gst_element = __ms_element_create(DEFAULT_VIDEO_CONVERT, NULL);
- break;
- case MEDIA_STREAMER_NODE_TYPE_AUDIO_CONVERTER:
- node->gst_element = __ms_element_create(DEFAULT_AUDIO_CONVERT, NULL);
- break;
- case MEDIA_STREAMER_NODE_TYPE_AUDIO_RESAMPLE:
- node->gst_element = __ms_element_create(DEFAULT_AUDIO_RESAMPLE, NULL);
- break;
- default:
- ms_error("Error: invalid node Type [%d]", node->type);
- break;
- }
+ node->gst_element = __ms_node_element_create(&plug_info, node->type);
+ if (node->gst_element)
+ node->name = gst_element_get_name(node->gst_element);
+ else
+ ret = MEDIA_STREAMER_ERROR_INVALID_OPERATION;
- MS_SAFE_FREE(plugin_name);
- MS_SAFE_FREE(format_prefix);
- __ms_destroy_ini_dictionary(dict);
+ if (src_caps)
+ gst_caps_unref(src_caps);
- if (node->gst_element == NULL)
- ret = MEDIA_STREAMER_ERROR_INVALID_OPERATION;
- else
- node->name = gst_element_get_name(node->gst_element);
+ if (sink_caps)
+ gst_caps_unref(sink_caps);
return ret;
}
ms_retvm_if(ini_path == NULL, NULL, "Invalid ini path");
if (dict == NULL) {
- result_str = g_strdup(default_str);
+ result_str = default_str;
} else {
gchar *str = NULL;
str = iniparser_getstring(dict, ini_path, default_str);
if (str && (strlen(str) > 0) && (strlen(str) < MEDIA_STREAMER_INI_MAX_STRLEN))
- result_str = g_strdup(str);
+ result_str = str;
else
- result_str = g_strdup(default_str);
+ result_str = default_str;
}
- return result_str;
+ return result_str ? g_strdup(result_str) : NULL;
}
gboolean __ms_load_ini_dictionary(dictionary **dict)
return TRUE;
}
-static void __ms_ini_read_list(dictionary *dict, const char* key, gchar ***list)
+void __ms_ini_read_list(dictionary *dict, const char* key, gchar ***list)
{
ms_retm_if(!dict || !list || !key, "Handle is NULL");