From: Volodymyr Brynza Date: Tue, 30 Aug 2016 10:57:03 +0000 (+0300) Subject: Adaptive streaming client basic implementation X-Git-Tag: submit/tizen/20160907.074000^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=6507c269815faa69ffb21be404d2ea66b6c5173f;p=platform%2Fcore%2Fapi%2Fmediastreamer.git Adaptive streaming client basic implementation Change-Id: Ia5f29a521f4d3838fa36d4107bc518aac5dcd4ce Signed-off-by: Volodymyr Brynza --- diff --git a/include/media_streamer.h b/include/media_streamer.h index dfc3cf6..6275c69 100644 --- a/include/media_streamer.h +++ b/include/media_streamer.h @@ -102,7 +102,8 @@ typedef enum { MEDIA_STREAMER_NODE_SRC_TYPE_VIDEO_CAPTURE, /**< Video capture src type, Camera feature is required */ MEDIA_STREAMER_NODE_SRC_TYPE_AUDIO_TEST, /**< Audio test src type */ MEDIA_STREAMER_NODE_SRC_TYPE_VIDEO_TEST, /**< Video test src type */ - MEDIA_STREAMER_NODE_SRC_TYPE_CUSTOM /**< Custom src type */ + MEDIA_STREAMER_NODE_SRC_TYPE_CUSTOM, /**< Custom src type */ + MEDIA_STREAMER_NODE_SRC_TYPE_ADAPTIVE /**< Adaptive src type */ } media_streamer_node_src_type_e; /** @@ -1063,7 +1064,7 @@ int media_streamer_node_get_pad_format(media_streamer_node_h node, const char *p * @brief Gets name of node pads. * @since_tizen 3.0 * @remark After using the src_pad_name and sink_pad_name, it have to be free. - * src_pad_name or sink_pad_name can be null accoring to the node type. + * src_pad_name or sink_pad_name can be null according to the node type. * In case of src type node, sink_pad_name will be null. * In case of sink type node, src_pad_name will be null. * @param [in] node Media streamer node handle @@ -1139,7 +1140,7 @@ int media_streamer_node_get_params(media_streamer_node_h node, bundle **param_li * The externalstorage privilege(http://tizen.org/privilege/externalstorage) should be added if any video/audio files are written in the external storage devices. * @param [in] node Media streamer node handle * @param [in] param_name Param name of node - * @param [in] param_value Parm value of node + * @param [in] param_value Param value of node * @return @c 0 on success, * otherwise a negative error value * @retval #MEDIA_STREAMER_ERROR_NONE Successful @@ -1165,7 +1166,7 @@ int media_streamer_node_set_param(media_streamer_node_h node, * @since_tizen 3.0 * @param [in] node Media streamer node handle * @param [in] param_name Param name of node - * @param [out] param_value Parm value of node + * @param [out] param_value Param value of node * @return @c 0 on success, * otherwise a negative error value * @retval #MEDIA_STREAMER_ERROR_NONE Successful diff --git a/include/media_streamer_gst.h b/include/media_streamer_gst.h index 26a6cc1..dbccb31 100644 --- a/include/media_streamer_gst.h +++ b/include/media_streamer_gst.h @@ -74,6 +74,20 @@ GstElement *__ms_bin_find_element_by_klass(GstElement *sink_bin, GstElement *pre */ GstElement *__ms_element_create(const char *plugin_name, const char *name); +/** + * @brief Creates GstBin name for adaptive streaming. + * + * @since_tizen 3.0 + */ +GstElement *__ms_adaptive_element_create(void); + +/** + * @brief Prepares GstBin for adaptive streaming. + * + * @since_tizen 3.0 + */ +int __ms_adaptive_element_prepare(media_streamer_node_s *ms_node, bool auto_plug); + /** * @brief Creates GstElement from specified node_plug_s structure. * @@ -102,6 +116,13 @@ GstElement *__ms_video_decoder_element_create(node_plug_s *plug_info, media_stre */ GstElement *__ms_audio_encoder_element_create(node_plug_s *plug_info, media_streamer_node_type_e type); +/** + * @brief Creates audio decoder GstElement. + * + * @since_tizen 3.0 + */ +GstElement *__ms_audio_decoder_element_create(node_plug_s *plug_info, media_streamer_node_type_e type); + /** * @brief Creates rtp container GstElement. * @@ -246,3 +267,10 @@ int __ms_element_push_packet(GstElement *src_element, media_packet_h packet); * @since_tizen 3.0 */ int __ms_element_pull_packet(GstElement *sink_element, media_packet_h *packet); + +/** + * @brief Prepare demuxer elemetn for playing. + * + * @since_tizen 3.0 + */ +int __ms_demux_element_prepare(media_streamer_s *ms_streamer, media_streamer_node_s *demux_node); diff --git a/include/media_streamer_util.h b/include/media_streamer_util.h index 01ecb96..74de8ba 100644 --- a/include/media_streamer_util.h +++ b/include/media_streamer_util.h @@ -183,6 +183,9 @@ typedef struct { #define DEFAULT_AUDIO_RTPPAY "rtpamrpay" #define DEFAULT_AUDIO_RTPDEPAY "rtpamrdepay" +/* adaptive streaming default */ +#define DEFAULT_ADAPTIVE_SOURCE "hlsdemux" + #define MEDIA_STREAMER_DEFAULT_CAMERA_FORMAT "video/x-raw,format=I420,width=352,height=288" #define MEDIA_STREAMER_DEFAULT_AUDIO_RAW_FORMAT "audio/x-raw,channels=1,rate=8000,format=S16LE" #define MEDIA_STREAMER_DEFAULT_VIDEO_FORMAT "video/x-h263,width=352,height=288,framerate = 3/1" @@ -330,6 +333,13 @@ void __ms_signal_destroy(void *data); */ void __ms_param_value_destroy(gpointer data); +/** + * @brief Check URI is valid file + * + * @since_tizen 3.0 + */ +int __ms_node_uri_path_check(const char *file_uri); + #ifdef __cplusplus } #endif diff --git a/src/media_streamer_gst.c b/src/media_streamer_gst.c index 4d2f53c..dd3e0d0 100644 --- a/src/media_streamer_gst.c +++ b/src/media_streamer_gst.c @@ -454,6 +454,7 @@ static gint __decodebin_autoplug_select_cb(GstElement * bin, GstPad * pad, GstCa return GST_AUTOPLUG_SELECT_SKIP; } } + } return result; @@ -695,6 +696,116 @@ GstElement *__ms_element_create(const char *plugin_name, const char *name) return plugin_elem; } +static void __hlsdemux_pad_added_cb(GstElement *demux, GstPad *pad, gpointer data) +{ + GstPad *gp = GST_PAD(data); + gst_ghost_pad_set_target (GST_GHOST_PAD(gp), pad); +} + +GstElement *__ms_adaptive_element_create(void) +{ + GstElement *adaptive_bin = gst_bin_new("adaptive_src"); + ms_retvm_if(!adaptive_bin, (GstElement *) NULL, "Error: creating elements for adaptive source"); + + __ms_add_no_target_ghostpad(adaptive_bin, "src", GST_PAD_SRC); + + /* Add adaptive node parameters as GObject data with destroy function */ + MS_SET_INT_STATIC_STRING_PARAM(adaptive_bin, MEDIA_STREAMER_PARAM_URI, "http://localhost"); + + return adaptive_bin; +} + +static GstElement *__ms_manifest_src_create(media_streamer_node_s *ms_node) +{ + char *manifest_src_name = NULL; + gchar *location = NULL; + GstElement *manifest_src = NULL; + + GValue *val = (GValue *)g_object_get_data(G_OBJECT(ms_node->gst_element), MEDIA_STREAMER_PARAM_URI); + const char *uri = g_value_get_string(val); + gchar *protocol = gst_uri_is_valid(uri) ? gst_uri_get_protocol(uri) : NULL; + + if (protocol && g_strrstr(protocol, "http")) { + manifest_src_name = __ms_ini_get_string("node type 1:http", DEFAULT_HTTP_SOURCE); + location = g_strdup(uri); + } else if (protocol && g_strrstr(protocol, "file")) { + manifest_src_name = __ms_ini_get_string("node type 1:file", DEFAULT_FILE_SOURCE); + location = gst_uri_get_location(uri); + } else { + ms_error("Unsupported URI protocol... Check URI is file path"); + if (__ms_node_uri_path_check(uri) == MEDIA_STREAMER_ERROR_NONE) { + manifest_src_name = __ms_ini_get_string("node type 1:file", DEFAULT_FILE_SOURCE); + location = g_strdup(uri); + } else { + g_free(protocol); + ms_error("URI is not valid file path"); + return NULL; + } + } + g_free(protocol); + ms_retvm_if(manifest_src_name == NULL, NULL, + "Error empty manifest source name for adaprive source"); + manifest_src = gst_element_factory_make(manifest_src_name, NULL); + ms_retvm_if(manifest_src == NULL, NULL, + "Error creating manifest source for adaptive source"); + g_object_set(manifest_src, "location", location, NULL); + g_free(location); + + return manifest_src; +} + +int __ms_adaptive_element_prepare(media_streamer_node_s *ms_node, bool auto_plug) +{ + char *plugin_name = NULL; + GstElement *manifest_src = NULL; + GstElement *plugin_elem = NULL; + gboolean res = FALSE; + GstPad *gp = NULL; + + ms_retvm_if(ms_node == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "Handle is NULL"); + + if (!auto_plug) { + plugin_name = __ms_ini_get_string("node type 1:adaptive", DEFAULT_ADAPTIVE_SOURCE); + ms_retvm_if(plugin_name == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "Error empty plugin name for adaprive source"); + ms_info("Creating [%s] element", plugin_name); + plugin_elem = gst_element_factory_make(plugin_name, NULL); + ms_retvm_if(plugin_elem == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, + "Error creating element [%s] for adaptive source", plugin_name); + + res = gst_bin_add(GST_BIN(ms_node->gst_element), plugin_elem); + ms_retvm_if(res == FALSE, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, + "Error adding adaptive element to bin for adaptive source"); + } + + manifest_src = __ms_manifest_src_create(ms_node); + ms_retvm_if(manifest_src == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, + "Error creating manifest source for adaptive source"); + + res = gst_bin_add(GST_BIN(ms_node->gst_element), manifest_src); + ms_retvm_if(res == FALSE, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, + "Error adding manifest source to bin for adaptive source"); + + if (!auto_plug) { + res = gst_element_link(manifest_src, plugin_elem); + ms_retvm_if(res == FALSE, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, + "Error linking manifest source and element for adaptive source"); + } + + gp = gst_element_get_static_pad(ms_node->gst_element, "src"); + ms_retvm_if(gp == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, + "Error getting source pad for adaptive source"); + + if (!auto_plug) { + g_signal_connect_object(plugin_elem, "pad-added", + G_CALLBACK(__hlsdemux_pad_added_cb), gp, 0); + } else { + GstPad *manifest_src_pad = gst_element_get_static_pad(manifest_src, "src"); + gst_ghost_pad_set_target (GST_GHOST_PAD(gp), manifest_src_pad); + } + + return MEDIA_STREAMER_ERROR_NONE; +} + static gboolean __ms_feature_node_filter(GstPluginFeature *feature, gpointer data) { node_plug_s *plug_info = (node_plug_s*)data; @@ -825,6 +936,8 @@ GstElement *__ms_node_element_create(node_plug_s *plug_info, media_streamer_node * element will be created immediately by format ot name */ if (type == MEDIA_STREAMER_NODE_TYPE_AUDIO_ENCODER) gst_element = __ms_audio_encoder_element_create(plug_info, type); + else if (type == MEDIA_STREAMER_NODE_TYPE_AUDIO_DECODER) + gst_element = __ms_audio_decoder_element_create(plug_info, type); else if (type == MEDIA_STREAMER_NODE_TYPE_VIDEO_ENCODER) gst_element = __ms_video_encoder_element_create(plug_info, type); else if (type == MEDIA_STREAMER_NODE_TYPE_VIDEO_DECODER) @@ -1027,6 +1140,60 @@ GstElement *__ms_audio_encoder_element_create(node_plug_s *plug_info, media_stre return audio_enc_bin; } +GstElement *__ms_audio_decoder_element_create(node_plug_s *plug_info, media_streamer_node_type_e type) +{ + GstCaps *dec_caps = plug_info->sink_caps; + if (!dec_caps) { + dec_caps = gst_caps_from_string(MEDIA_STREAMER_DEFAULT_AUDIO_FORMAT); + ms_debug("No Audio decoding format is set! Deafault will be: [%s]", MEDIA_STREAMER_DEFAULT_AUDIO_FORMAT); + } + + /* Creating Audio Decoder */ + node_info_s *node_klass_type = __ms_node_get_klass_by_its_type(type); + node_plug_s decoder_info = {node_klass_type, plug_info->src_caps, dec_caps, NULL}; + GstElement *decoder_elem = __ms_element_create_from_ini(&decoder_info, MEDIA_STREAMER_NODE_TYPE_AUDIO_DECODER); + if (!decoder_elem) + decoder_elem = __ms_element_create_by_registry(&decoder_info, MEDIA_STREAMER_NODE_TYPE_AUDIO_DECODER); + + /* Creating Audio Parser */ + node_info_s nodes_info = {MEDIA_STREAMER_PARSER_KLASS, DEFAULT_AUDIO_PARSER}; + node_plug_s parser_info = {&nodes_info, dec_caps, dec_caps, NULL}; + GstElement *decoder_parser = __ms_element_create_from_ini(&parser_info, MEDIA_STREAMER_NODE_TYPE_PARSER); + if (!decoder_parser) + decoder_parser = __ms_element_create_by_registry(&parser_info, MEDIA_STREAMER_NODE_TYPE_PARSER); + + /* Creating bin - Audio Decoder */ + gboolean gst_ret = FALSE; + GstElement *decoder_bin = gst_bin_new("audio_decoder"); + GstElement *decoder_queue = __ms_element_create("queue", NULL); + ms_retvm_if(!decoder_elem || !decoder_queue || !decoder_bin || !decoder_parser, (GstElement *) NULL, "Error: creating elements for audio decoder bin"); + + /* Adding elements to bin Audio Encoder */ + gst_bin_add_many(GST_BIN(decoder_bin), decoder_queue, decoder_elem, decoder_parser, NULL); + gst_ret = gst_element_link_many(decoder_queue, decoder_parser, decoder_elem, NULL); + if (gst_ret != TRUE) { + ms_error("Failed to link elements into decoder_bin"); + MS_SAFE_UNREF(decoder_bin); + return NULL; + } + + GstElement *audio_conv = __ms_element_create("audioconvert", NULL); + GstElement *audio_resample = __ms_element_create("audioresample", NULL); + ms_retvm_if(!audio_conv || !audio_resample, (GstElement *) NULL, "Error: creating elements for audio decoder bin"); + gst_bin_add_many(GST_BIN(decoder_bin), audio_conv, audio_resample, NULL); + gst_ret = gst_element_link_many(decoder_elem, audio_conv, audio_resample, NULL); + if (gst_ret != TRUE) { + ms_error("Failed to link elements into decoder_bin"); + MS_SAFE_UNREF(decoder_bin); + return NULL; + } + + __ms_add_ghostpad(audio_resample, "src", decoder_bin, "src"); + __ms_add_ghostpad(decoder_queue, "sink", decoder_bin, "sink"); + + return decoder_bin; +} + GstElement *__ms_rtp_element_create(void) { GstElement *rtp_container = gst_bin_new("rtp_container"); @@ -1491,6 +1658,11 @@ GstCaps *__ms_create_caps_from_fmt(media_format_h fmt) caps_name = gst_caps_to_string(caps); ms_info("Creating Video Caps from media format [%s]", caps_name); + } else if (!media_format_get_container_mime(fmt, &mime)) { + caps = gst_caps_new_empty_simple(__ms_convert_mime_to_string_format(mime)); + caps_name = gst_caps_to_string(caps); + ms_info("Creating Video Caps from media format [%s]", caps_name); + } else ms_error("Error getting media format information"); @@ -1785,3 +1957,61 @@ int __ms_element_pull_packet(GstElement *sink_element, media_packet_h *packet) gst_sample_unref(sample); return ret; } + +static void __demux_newpad_cb(GstElement * demux, GstPad * new_pad, gpointer user_data) +{ + media_streamer_s *ms_streamer = (media_streamer_s *) user_data; + ms_retm_if(ms_streamer == NULL, "Handle is NULL"); + + g_mutex_lock(&ms_streamer->mutex_lock); + + g_object_ref(new_pad); + ms_streamer->pads_types_list = g_list_insert_sorted(ms_streamer->pads_types_list, new_pad, __pad_type_compare); + + g_mutex_unlock(&ms_streamer->mutex_lock); +} + +static void __demux_nomore_pads_combine(GstPad *src_pad, media_streamer_s *ms_streamer) +{ + GstElement *found_element = gst_pad_get_parent_element(src_pad); + const gchar *new_pad_type = __ms_get_pad_type(src_pad); + if (MS_ELEMENT_IS_VIDEO(new_pad_type)) { + found_element = __ms_combine_next_element(found_element, src_pad, ms_streamer->topology_bin, MEDIA_STREAMER_NODE_TYPE_VIDEO_DECODER); + } else if (MS_ELEMENT_IS_AUDIO(new_pad_type)) { + found_element = __ms_combine_next_element(found_element, src_pad, ms_streamer->topology_bin, MEDIA_STREAMER_NODE_TYPE_AUDIO_DECODER); + } else if (MS_ELEMENT_IS_TEXT(new_pad_type)) { + found_element = __ms_combine_next_element(found_element, src_pad, ms_streamer->topology_bin, MEDIA_STREAMER_NODE_TYPE_TEXT_OVERLAY); + } else { + ms_error("Unsupported pad type [%s]!", new_pad_type); + } + __ms_generate_dots(ms_streamer->pipeline, "after_demux_linked"); + gst_object_unref(found_element); +} + +static void __demux_nomore_pads_cb(GstElement *demux, gpointer user_data) +{ + media_streamer_s *ms_streamer = (media_streamer_s *) user_data; + ms_retm_if(ms_streamer == NULL, "Handle is NULL"); + + g_mutex_lock(&ms_streamer->mutex_lock); + + GList *iterator = NULL; + GList *list = ms_streamer->pads_types_list; + for (iterator = list; iterator; iterator = iterator->next) { + GstPad *src_pad = GST_PAD(iterator->data); + __demux_nomore_pads_combine(src_pad, ms_streamer); + } + + g_mutex_unlock(&ms_streamer->mutex_lock); +} + +int __ms_demux_element_prepare(media_streamer_s * ms_streamer, media_streamer_node_s *demux_node) +{ + ms_retvm_if(!ms_streamer, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "Handle is NULL"); + ms_retvm_if(!demux_node, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "Handle is NULL"); + + __ms_signal_create(&ms_streamer->autoplug_sig_list, demux_node->gst_element, "pad-added", G_CALLBACK(__demux_newpad_cb), ms_streamer); + __ms_signal_create(&ms_streamer->autoplug_sig_list, demux_node->gst_element, "no-more-pads", G_CALLBACK(__demux_nomore_pads_cb), ms_streamer); + + return MEDIA_STREAMER_ERROR_NONE; +} diff --git a/src/media_streamer_node.c b/src/media_streamer_node.c index ff86275..0ca0464 100644 --- a/src/media_streamer_node.c +++ b/src/media_streamer_node.c @@ -17,8 +17,6 @@ #include #include #include -#include -#include #include #define SMACK_LABEL_LEN 255 @@ -168,6 +166,62 @@ static int __ms_rtp_node_set_property(media_streamer_node_s *ms_node, param_s *p return ret; } +static gboolean __ms_adaptive_src_node_has_property(media_streamer_node_s *ms_node, const char * param_name) +{ + ms_retvm_if(!ms_node || !ms_node->gst_element, FALSE, "Error: empty node"); + ms_retvm_if(!param_name, FALSE, "Error: invalid property parameter"); + + if (ms_node->type == MEDIA_STREAMER_NODE_TYPE_SRC && + ms_node->subtype == MEDIA_STREAMER_NODE_SRC_TYPE_ADAPTIVE) { + GValue *val = (GValue *)g_object_get_data(G_OBJECT(ms_node->gst_element), param_name); + return val ? TRUE : FALSE; + } + + return FALSE; +} + +static int __ms_adaptive_src_node_get_property(media_streamer_node_s *ms_node, param_s *param, GValue *value) +{ + ms_retvm_if(!ms_node || !ms_node->gst_element, FALSE, "Error: empty node"); + ms_retvm_if(ms_node->type != MEDIA_STREAMER_NODE_TYPE_SRC && + ms_node->subtype != MEDIA_STREAMER_NODE_SRC_TYPE_ADAPTIVE, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "Invalid node type"); + ms_retvm_if(!param, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "Error: invalid property parameter"); + + int ret = MEDIA_STREAMER_ERROR_NONE; + + GValue *val = (GValue *)g_object_get_data(G_OBJECT(ms_node->gst_element), param->param_name); + if (!strcmp(param->param_name, MEDIA_STREAMER_PARAM_URI)) + g_value_init(value, G_TYPE_STRING); + else + ret = MEDIA_STREAMER_ERROR_INVALID_PARAMETER; + + g_value_copy(val, value); + return ret; +} + +static int __ms_adaptive_src_node_set_property(media_streamer_node_s *ms_node, param_s *param, const char *param_value) +{ + ms_retvm_if(!ms_node || !ms_node->gst_element, MEDIA_STREAMER_ERROR_INVALID_OPERATION, "Error: empty node"); + ms_retvm_if(ms_node->type != MEDIA_STREAMER_NODE_TYPE_SRC && + ms_node->subtype != MEDIA_STREAMER_NODE_SRC_TYPE_ADAPTIVE, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "Invalid node type"); + ms_retvm_if(!param, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "Error: invalid property parameter"); + + int ret = MEDIA_STREAMER_ERROR_NONE; + + GValue *val = (GValue *)g_object_get_data(G_OBJECT(ms_node->gst_element), param->param_name); + if (!val) + ret = MEDIA_STREAMER_ERROR_INVALID_PARAMETER; + + if (!strcmp(param->param_name, MEDIA_STREAMER_PARAM_URI)) { + g_value_unset(val); + g_value_init(val, G_TYPE_STRING); + g_value_set_string(val, param_value); + } else + ret = MEDIA_STREAMER_ERROR_INVALID_PARAMETER; + + return ret; +} + int __ms_node_create(media_streamer_node_s *node, media_format_h in_fmt, media_format_h out_fmt) { ms_retvm_if(node == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "Handle is NULL"); @@ -235,6 +289,9 @@ static int __ms_node_check_priveleges(media_streamer_node_s *node) case MEDIA_STREAMER_NODE_SRC_TYPE_HTTP: privilege = "http://tizen.org/privilege/internet"; break; + case MEDIA_STREAMER_NODE_SRC_TYPE_ADAPTIVE: + privilege = "http://tizen.org/privilege/internet"; + break; case MEDIA_STREAMER_NODE_SRC_TYPE_CAMERA: privilege = "http://tizen.org/privilege/camera"; break; @@ -367,6 +424,9 @@ int __ms_src_node_create(media_streamer_node_s *node) __ms_signal_create(&node->sig_list, node->gst_element, "need-data", G_CALLBACK(__ms_src_start_feed_cb), node); __ms_signal_create(&node->sig_list, node->gst_element, "enough-data", G_CALLBACK(__ms_src_stop_feed_cb), node); break; + case MEDIA_STREAMER_NODE_SRC_TYPE_ADAPTIVE: + node->gst_element = __ms_adaptive_element_create(); + break; default: ms_error("Error: invalid Src node Type [%d]", node->subtype); break; @@ -604,14 +664,22 @@ static void _src_node_prepare(const GValue *item, gpointer user_data) MS_SAFE_UNREF(src_pad); } +static gboolean demux_find(gpointer key, gpointer value, gpointer user_data) { + return g_strrstr((char *)key, "demux") != NULL; +} + int __ms_pipeline_prepare(media_streamer_s *ms_streamer) { ms_retvm_if(ms_streamer == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "Handle is NULL"); int ret = MEDIA_STREAMER_ERROR_NONE; media_streamer_node_s *rtp_node = (media_streamer_node_s *)g_hash_table_lookup(ms_streamer->nodes_table, "rtp_container"); + media_streamer_node_s *demux = (media_streamer_node_s *)g_hash_table_find(ms_streamer->nodes_table, (GHRFunc)demux_find, NULL); + media_streamer_node_s *adaptive_src = (media_streamer_node_s *)g_hash_table_lookup(ms_streamer->nodes_table, "adaptive_src"); if (rtp_node) { ret = __ms_rtp_element_prepare(rtp_node) ? MEDIA_STREAMER_ERROR_NONE : MEDIA_STREAMER_ERROR_INVALID_PARAMETER; + } else if (demux) { + ret = __ms_demux_element_prepare(ms_streamer, demux) ? MEDIA_STREAMER_ERROR_NONE : MEDIA_STREAMER_ERROR_INVALID_PARAMETER; } else { GstBin *nodes_bin = GST_BIN(ms_streamer->src_bin); if (nodes_bin->numchildren == 0) { @@ -625,6 +693,14 @@ int __ms_pipeline_prepare(media_streamer_s *ms_streamer) } } + if (adaptive_src) { + if (GST_BIN(ms_streamer->topology_bin)->numchildren == 0) { + ret = __ms_adaptive_element_prepare(adaptive_src, true); + } else { + ret = __ms_adaptive_element_prepare(adaptive_src, false); + } + } + MS_BIN_FOREACH_ELEMENTS(ms_streamer->sink_bin, __ms_element_lock_state, ms_streamer); MS_BIN_FOREACH_ELEMENTS(ms_streamer->src_bin, _src_node_prepare, ms_streamer); @@ -780,7 +856,8 @@ int __ms_node_get_param(media_streamer_node_s *node, const char *param_name, par for (it_param = 0; param_table[it_param].param_name != NULL; it_param++) { if (!g_strcmp0(param_name, param_table[it_param].param_name)) { param_spec = g_object_class_find_property(G_OBJECT_GET_CLASS(node->gst_element), param_table[it_param].origin_name); - if (param_spec || __ms_rtp_node_has_property(node, param_name)) { + if (param_spec || __ms_rtp_node_has_property(node, param_table[it_param].origin_name) || + __ms_adaptive_src_node_has_property(node, param_table[it_param].origin_name)) { *param = &(param_table[it_param]); ms_info("Got parameter [%s] for node [%s]", (*param)->param_name, node->name); found_param = TRUE; @@ -799,7 +876,8 @@ int __ms_node_get_param_list(media_streamer_node_s *node, GList **param_list) for (it_param = 0; param_table[it_param].param_name != NULL; it_param++) { param_spec = g_object_class_find_property(G_OBJECT_GET_CLASS(node->gst_element), param_table[it_param].origin_name); - if (param_spec || __ms_rtp_node_has_property(node, param_table[it_param].param_name)) { + if (param_spec || __ms_rtp_node_has_property(node, param_table[it_param].origin_name) || + __ms_adaptive_src_node_has_property(node, param_table[it_param].origin_name)) { ms_info("Got parameter [%s] for node [%s]", param_table[it_param].param_name, node->name); *param_list = g_list_append(*param_list, &(param_table[it_param])); } @@ -818,6 +896,9 @@ int __ms_node_get_param_value(media_streamer_node_s *node, param_s *param, char if (node->type == MEDIA_STREAMER_NODE_TYPE_RTP) ret = __ms_rtp_node_get_property(node, param, &value); + else if (node->type == MEDIA_STREAMER_NODE_TYPE_SRC && + node->subtype == MEDIA_STREAMER_NODE_SRC_TYPE_ADAPTIVE) + ret = __ms_adaptive_src_node_get_property(node, param, &value); else { param_spec = g_object_class_find_property(G_OBJECT_GET_CLASS(node->gst_element), param->origin_name); if (param_spec) { @@ -891,46 +972,6 @@ int __ms_node_get_param_value(media_streamer_node_s *node, param_s *param, char return ret; } -int __ms_node_uri_path_check(const char *file_uri) -{ - struct stat stat_results = {0, }; - int file_open = 0; - - if (!file_uri || !strlen(file_uri)) - return MEDIA_STREAMER_ERROR_INVALID_PARAMETER; - - /* We can not check wheter the content is available over http or other protocol - * Thus, here we have to check occurence of :// */ - if (g_strrstr_len(file_uri, DEFAULT_URI_SCHEME_LENGTH, "://")) - return MEDIA_STREAMER_ERROR_NONE; - - file_open = open(file_uri, O_RDONLY); - if (file_open < 0) { - char mes_error[256]; - strerror_r(errno, mes_error, sizeof(mes_error)); - ms_error("Couldn`t open file according to [%s]. Error N [%d]", mes_error, errno); - - if (EACCES == errno) - return MEDIA_STREAMER_ERROR_PERMISSION_DENIED; - - return MEDIA_STREAMER_ERROR_INVALID_PARAMETER; - } - - if (fstat(file_open, &stat_results) < 0) { - ms_error("Couldn`t get status of the file [%s]", file_uri); - } else if (stat_results.st_size == 0) { - ms_error("The size of file is 0"); - close(file_open); - return MEDIA_STREAMER_ERROR_INVALID_PARAMETER; - } else { - ms_debug("Size of file [%lld] bytes", (long long)stat_results.st_size); - } - - close(file_open); - - return MEDIA_STREAMER_ERROR_NONE; -} - int __ms_node_set_param_value(media_streamer_node_s *ms_node, param_s *param, const char *param_value) { ms_retvm_if(!ms_node || !param || !param_value, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "Handle is NULL"); @@ -942,6 +983,12 @@ int __ms_node_set_param_value(media_streamer_node_s *ms_node, param_s *param, co return ret; } + if (ms_node->type == MEDIA_STREAMER_NODE_TYPE_SRC && + ms_node->subtype == MEDIA_STREAMER_NODE_SRC_TYPE_ADAPTIVE) { + ret = __ms_adaptive_src_node_set_property(ms_node, param, param_value); + return ret; + } + if (!g_strcmp0(param->param_name, MEDIA_STREAMER_PARAM_CAMERA_ID)) { int camera_id = (int)strtol(param_value, NULL, 10); ms_retvm_if(camera_id == -1, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "Invalid %s value", param->param_name); diff --git a/src/media_streamer_util.c b/src/media_streamer_util.c index dcb1d60..d639070 100644 --- a/src/media_streamer_util.c +++ b/src/media_streamer_util.c @@ -19,6 +19,9 @@ #include #include +#include +#include + format_s format_table[] = { /* Audio - ENCODED */ {MEDIA_FORMAT_L16, "audio/x-raw"}, @@ -70,6 +73,9 @@ format_s format_table[] = { {MEDIA_FORMAT_RGBA, "RGBA"}, {MEDIA_FORMAT_ARGB, "ARGB"}, {MEDIA_FORMAT_NATIVE_VIDEO, "NATIVE_VIDEO"}, + /* Container */ + {MEDIA_FORMAT_CONTAINER_MP4, "video/quicktime"}, + {MEDIA_FORMAT_CONTAINER_MPEG2TS, "video/mpegts"}, {MEDIA_FORMAT_MAX, NULL} }; @@ -279,3 +285,38 @@ void __ms_param_value_destroy(gpointer data) } MS_SAFE_GFREE(val); } + +int __ms_node_uri_path_check(const char *file_uri) +{ + struct stat stat_results = {0, }; + int file_open = 0; + + if (!file_uri || !strlen(file_uri)) + return MEDIA_STREAMER_ERROR_INVALID_PARAMETER; + + file_open = open(file_uri, O_RDONLY); + if (file_open < 0) { + char mes_error[256]; + strerror_r(errno, mes_error, sizeof(mes_error)); + ms_error("Couldn`t open file according to [%s]. Error N [%d]", mes_error, errno); + + if (EACCES == errno) + return MEDIA_STREAMER_ERROR_PERMISSION_DENIED; + + return MEDIA_STREAMER_ERROR_INVALID_PARAMETER; + } + + if (fstat(file_open, &stat_results) < 0) { + ms_error("Couldn`t get status of the file [%s]", file_uri); + } else if (stat_results.st_size == 0) { + ms_error("The size of file is 0"); + close(file_open); + return MEDIA_STREAMER_ERROR_INVALID_PARAMETER; + } else { + ms_debug("Size of file [%lld] bytes", (long long)stat_results.st_size); + } + + close(file_open); + + return MEDIA_STREAMER_ERROR_NONE; +} diff --git a/test/media_streamer_test.c b/test/media_streamer_test.c index bf6aad6..4974e5f 100644 --- a/test/media_streamer_test.c +++ b/test/media_streamer_test.c @@ -28,7 +28,8 @@ typedef enum { MENU_STATE_BROADCAST_MENU, MENU_STATE_VOIP_MENU, MENU_STATE_PLAYING_MENU, - MENU_STATE_PRESET_MENU + MENU_STATE_PRESET_MENU, + MENU_STATE_ADAPTIVE_MENU, } menu_state_e; typedef enum { @@ -40,7 +41,8 @@ typedef enum { SUBMENU_STATE_AUTOPLUG, SUBMENU_STATE_SCENARIO, SUBMENU_STATE_PLAYING_SCENARIO, - SUBMENU_STATE_FORMAT + SUBMENU_STATE_FORMAT, + SUBMENU_STATE_ADAPTIVE_SCENARIO, } submenu_state_e; #define SECOND_VOIP_MASK 0x8 @@ -70,7 +72,10 @@ typedef enum { SCENARIO_MODE_FILE_PLAY_VIDEO_AUDIO, SCENARIO_MODE_FILE_SUBTITLE_VIDEO_AUDIO, SCENARIO_MODE_HTTP_VIDEO_AUDIO, - SCENARIO_MODE_APPSRC_APPSINK + SCENARIO_MODE_APPSRC_APPSINK, + SCENARIO_MODE_ADAPTIVE_SERVER, + SCENARIO_MODE_ADAPTIVE_CLIENT_AUTO, + SCENARIO_MODE_ADAPTIVE_CLIENT_MANUAL, } scenario_mode_e; #define PACKAGE "media_streamer_test" @@ -123,8 +128,11 @@ gboolean g_audio_is_on = FALSE; media_format_h vfmt_raw = NULL; media_format_h vfmt_encoded = NULL; +media_format_h vfmt_aenc = NULL; media_format_h afmt_raw = NULL; media_format_h afmt_encoded = NULL; +media_format_h afmt_aenc = NULL; +media_format_h tsfmt = NULL; static void streamer_error_cb(media_streamer_h streamer, media_streamer_error_e error, void *user_data) { @@ -297,6 +305,16 @@ static void create_formats(void) media_format_set_video_avg_bps(vfmt_encoded, VIDEO_AVG_BPS); media_format_set_video_max_bps(vfmt_encoded, VIDEO_MAX_BPS); + /* Define encoded video format for adaptive stream */ + media_format_create(&vfmt_aenc); + if (media_format_set_video_mime(vfmt_aenc, MEDIA_FORMAT_H264_SP) != MEDIA_FORMAT_ERROR_NONE) + g_print("media_format_set_video_mime failed!"); + + media_format_set_video_width(vfmt_aenc, VIDEO_WIDTH); + media_format_set_video_height(vfmt_aenc, VIDEO_HIGHT); + media_format_set_video_avg_bps(vfmt_aenc, VIDEO_AVG_BPS); + media_format_set_video_max_bps(vfmt_aenc, VIDEO_MAX_BPS); + /* Define audio raw format */ media_format_create(&afmt_raw); if (media_format_set_audio_mime(afmt_raw, MEDIA_FORMAT_PCM) != MEDIA_FORMAT_ERROR_NONE) @@ -312,6 +330,20 @@ static void create_formats(void) media_format_set_audio_channel(afmt_encoded, AUDIO_CHANNEL); media_format_set_audio_samplerate(afmt_encoded, AUDIO_SAMPLERATE); + + /* Define audio encoded format for adaptive stream */ + media_format_create(&afmt_aenc); + if (media_format_set_audio_mime(afmt_aenc, MEDIA_FORMAT_AAC) != MEDIA_FORMAT_ERROR_NONE) + g_print("media_format_set_audio_mime failed!"); + + media_format_set_audio_channel(afmt_aenc, AUDIO_CHANNEL); + media_format_set_audio_samplerate(afmt_aenc, AUDIO_SAMPLERATE); + media_format_set_audio_aac_type(afmt_aenc, TRUE); + + /* Define mpegts stream format */ + media_format_create(&tsfmt); + if (media_format_set_container_mime(tsfmt, MEDIA_FORMAT_CONTAINER_MPEG2TS) != MEDIA_FORMAT_ERROR_NONE) + g_print("media_format_set_container_mime failed!"); } static void set_rtp_params(media_streamer_node_h rtp_node, const char *ip, int video_port, int audio_port, gboolean port_reverse) @@ -640,6 +672,71 @@ static media_streamer_node_h _create_rtp(int video_port, int audio_port, gboolea return rtp_bin; } +static void _create_adaptive_playing() +{ + g_print("\n _create_adaptive_playing \n"); + media_streamer_node_h adaptive_src = NULL; + media_streamer_node_create_src(MEDIA_STREAMER_NODE_SRC_TYPE_ADAPTIVE, &adaptive_src); + media_streamer_node_set_param(adaptive_src, MEDIA_STREAMER_PARAM_URI, g_uri); + media_streamer_node_add(current_media_streamer, adaptive_src); + APPEND_NODE(adaptive_src); + + /*********************** videosink *********************************** */ + media_streamer_node_h video_sink = NULL; + media_streamer_node_create_sink(MEDIA_STREAMER_NODE_SINK_TYPE_OVERLAY, &video_sink); + media_streamer_node_add(current_media_streamer, video_sink); + APPEND_NODE(video_sink); + + /*********************** audiosink *********************************** */ + media_streamer_node_h audio_sink = NULL; + media_streamer_node_create_sink(MEDIA_STREAMER_NODE_SINK_TYPE_AUDIO, &audio_sink); + media_streamer_node_add(current_media_streamer, audio_sink); + APPEND_NODE(audio_sink); +} + +static void _create_adaptive_playing_manual() +{ + g_print("\n _create_adaptive_playing_manual \n"); + media_streamer_node_h adaptive_src = NULL; + media_streamer_node_create_src(MEDIA_STREAMER_NODE_SRC_TYPE_ADAPTIVE, &adaptive_src); + media_streamer_node_set_param(adaptive_src, MEDIA_STREAMER_PARAM_URI, g_uri); + media_streamer_node_add(current_media_streamer, adaptive_src); + APPEND_NODE(adaptive_src); + + media_streamer_node_h ts_demux = NULL; + media_streamer_node_create(MEDIA_STREAMER_NODE_TYPE_DEMUXER, tsfmt, NULL, &ts_demux); + media_streamer_node_add(current_media_streamer, ts_demux); + APPEND_NODE(ts_demux); + + media_streamer_node_h video_dec = NULL; + media_streamer_node_create(MEDIA_STREAMER_NODE_TYPE_VIDEO_DECODER, vfmt_aenc, vfmt_raw, &video_dec); + media_streamer_node_add(current_media_streamer, video_dec); + APPEND_NODE(video_dec); + + media_streamer_node_h audio_dec = NULL; + media_streamer_node_create(MEDIA_STREAMER_NODE_TYPE_AUDIO_DECODER, afmt_aenc, afmt_raw, &audio_dec); + media_streamer_node_add(current_media_streamer, audio_dec); + APPEND_NODE(audio_dec); + + /*********************** videosink *********************************** */ + media_streamer_node_h video_sink = NULL; + media_streamer_node_create_sink(MEDIA_STREAMER_NODE_SINK_TYPE_OVERLAY, &video_sink); + media_streamer_node_add(current_media_streamer, video_sink); + APPEND_NODE(video_sink); + + /*********************** audiosink *********************************** */ + media_streamer_node_h audio_sink = NULL; + media_streamer_node_create_sink(MEDIA_STREAMER_NODE_SINK_TYPE_AUDIO, &audio_sink); + media_streamer_node_add(current_media_streamer, audio_sink); + APPEND_NODE(audio_sink); + + /********************** link nodes *********************************** */ + media_streamer_node_link(adaptive_src, "src", ts_demux, "sink"); + media_streamer_node_link(video_dec, "src", video_sink, "sink"); + media_streamer_node_link(audio_dec, "src", audio_sink, "sink"); +} + + /* Application source callback */ static void buffer_status_cb(media_streamer_node_h node, media_streamer_custom_buffer_status_e status, void *user_data) { @@ -842,6 +939,23 @@ static void display_playing_scenario_select_menu(void) g_print("====================================================\n"); } +static void display_adaptive_scenario_select_menu(void) +{ + g_print("\n"); + g_print("====================================================\n"); + g_print(" media streamer test: Adaptive menu v0.3\n"); + g_print("----------------------------------------------------\n"); + g_print("\n"); + g_print("Please select Adaptive Scenario mode\n"); + g_print("By default will be used [%d] mode\n", g_scenario_mode); + g_print("1. [Server] Create Video http file segment \n"); + g_print("2. [Client] Adaptive playing auto-plug mode\n"); + g_print("3. [Client] Adaptive playing manual plug mode\n"); + g_print("b. back \n"); + g_print("----------------------------------------------------\n"); + g_print("====================================================\n"); +} + static void display_preset_menu(void) { g_print("\n"); @@ -901,6 +1015,7 @@ static void display_main_menu(void) g_print("1. Broadcast \n"); g_print("2. VOIP \n"); g_print("3. Local Playing \n"); + g_print("4. Adaptive \n"); g_print("q. quit \n"); g_print("----------------------------------------------------\n"); g_print("====================================================\n"); @@ -925,6 +1040,9 @@ static void display_menu(void) case MENU_STATE_PRESET_MENU: display_preset_menu(); break; + case MENU_STATE_ADAPTIVE_MENU: + display_preset_menu(); + break; default: g_print("*** Unknown status.\n"); break; @@ -952,6 +1070,9 @@ static void display_menu(void) case SUBMENU_STATE_PLAYING_SCENARIO: display_playing_scenario_select_menu(); break; + case SUBMENU_STATE_ADAPTIVE_SCENARIO: + display_adaptive_scenario_select_menu(); + break; default: g_print("*** Unknown Submenu state.\n"); break; @@ -1037,6 +1158,22 @@ void run_playing_preset(void) g_print("Invalid playing menu preset was selected!"); } +void run_adaptive_preset(void) +{ + create_formats(); + + g_print("%s:%d, %d %d %d", __FUNCTION__, __LINE__, g_menu_state, g_sub_menu_state, g_scenario_mode); + + if (g_scenario_mode == SCENARIO_MODE_ADAPTIVE_SERVER) + g_print("Adaptive server scenario"); + else if (g_scenario_mode == SCENARIO_MODE_ADAPTIVE_CLIENT_AUTO) + _create_adaptive_playing(); /* temp */ + else if (g_scenario_mode == SCENARIO_MODE_ADAPTIVE_CLIENT_MANUAL) + _create_adaptive_playing_manual(); /* temp */ + else + g_print("Invalid adaptive menu preset was selected!"); +} + void _interpret_main_menu(char *cmd) { int len = strlen(cmd); @@ -1048,6 +1185,8 @@ void _interpret_main_menu(char *cmd) g_menu_state = MENU_STATE_VOIP_MENU; else if (!strncmp(cmd, "3", len)) g_menu_state = MENU_STATE_PLAYING_MENU; + else if (!strncmp(cmd, "4", len)) + g_menu_state = MENU_STATE_ADAPTIVE_MENU; else if (!strncmp(cmd, "q", len)) quit(); } else { @@ -1112,6 +1251,28 @@ void _interpret_voip_menu(char *cmd) } } +void _interpret_adaptive_scenario_menu(char *cmd) +{ + int len = strlen(cmd); + + if (len == 1) { + if (!strncmp(cmd, "1", len)) { + g_scenario_mode = SCENARIO_MODE_ADAPTIVE_SERVER; + g_sub_menu_state = SUBMENU_STATE_GETTING_VIDEOFILE_URI; + return; + } else if (!strncmp(cmd, "2", len)) { + g_scenario_mode = SCENARIO_MODE_ADAPTIVE_CLIENT_AUTO; + g_sub_menu_state = SUBMENU_STATE_GETTING_VIDEOFILE_URI; + return; + } else if (!strncmp(cmd, "3", len)) { + g_scenario_mode = SCENARIO_MODE_ADAPTIVE_CLIENT_MANUAL; + g_sub_menu_state = SUBMENU_STATE_GETTING_VIDEOFILE_URI; + return; + } + } + g_sub_menu_state = SUBMENU_STATE_UNKNOWN; +} + void _interpret_playing_scenario_menu(char *cmd) { int len = strlen(cmd); @@ -1262,6 +1423,7 @@ void _interpret_getting_uri_menu(char *cmd) return; } + g_print("_interpret_getting_uri_menu %d %d %d", g_menu_state, g_sub_menu_state, g_scenario_mode); if (g_menu_state == MENU_STATE_PLAYING_MENU) { if (g_scenario_mode == SCENARIO_MODE_FILE_SUBTITLE_VIDEO_AUDIO) { g_sub_menu_state = SUBMENU_STATE_GETTING_SUBFILE_URI; @@ -1274,6 +1436,10 @@ void _interpret_getting_uri_menu(char *cmd) create_formats(); _create_file_streaming(); _create_rtp(VIDEO_PORT, AUDIO_PORT, FALSE); + } else if (g_scenario_mode == SCENARIO_MODE_ADAPTIVE_SERVER || + g_scenario_mode == SCENARIO_MODE_ADAPTIVE_CLIENT_AUTO || + g_scenario_mode == SCENARIO_MODE_ADAPTIVE_CLIENT_MANUAL) { + run_adaptive_preset(); } else { run_preset(); } @@ -1320,6 +1486,8 @@ void _interpret_preset_menu(char *cmd) /* call the run_preset function after autoplug mode was selected; */ if (g_menu_state == MENU_STATE_PLAYING_MENU) g_sub_menu_state = SUBMENU_STATE_PLAYING_SCENARIO; + else if (g_menu_state == MENU_STATE_ADAPTIVE_MENU) + g_sub_menu_state = SUBMENU_STATE_ADAPTIVE_SCENARIO; else g_sub_menu_state = SUBMENU_STATE_AUTOPLUG; } else if (!strncmp(cmd, "3", len)) { @@ -1372,6 +1540,9 @@ static void interpret_cmd(char *cmd) case MENU_STATE_PRESET_MENU: _interpret_preset_menu(cmd); break; + case MENU_STATE_ADAPTIVE_MENU: + _interpret_preset_menu(cmd); + break; default: g_print("Invalid command\n"); return; @@ -1400,6 +1571,9 @@ static void interpret_cmd(char *cmd) case SUBMENU_STATE_PLAYING_SCENARIO: _interpret_playing_scenario_menu(cmd); break; + case SUBMENU_STATE_ADAPTIVE_SCENARIO: + _interpret_adaptive_scenario_menu(cmd); + break; default: g_print("*** Unknown Submenu state.\n"); break;