X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fmm_player_streaming.c;h=07169a14e084f02e3ca03ef6d3899496ee34e63d;hb=1a6379fe9aa85fc0d6fc948c0658bbec6fd6e9a9;hp=adcb763d6e00bcbf113d4feb81f335fda74259cc;hpb=cb676d55f361485249d9bf4d26749125619ae206;p=platform%2Fcore%2Fmultimedia%2Flibmm-player.git diff --git a/src/mm_player_streaming.c b/src/mm_player_streaming.c old mode 100755 new mode 100644 index adcb763..07169a1 --- a/src/mm_player_streaming.c +++ b/src/mm_player_streaming.c @@ -20,418 +20,793 @@ * */ -#include +#include #include "mm_player_utils.h" - #include "mm_player_streaming.h" -static void streaming_set_buffer_size(mm_player_streaming_t* streamer, guint buffer_size); -static void streaming_set_buffer_percent(mm_player_streaming_t* streamer, gdouble low_percent, gdouble high_percent); -static void streaming_set_buffer_type (mm_player_streaming_t* streamer, gboolean use_file, gchar * file_path, guint64 content_size); -static void streaming_set_buffering_time(mm_player_streaming_t* streamer, gdouble buffering_time); +#define ESTIMATED_BUFFER_UNIT (1 * 1024 * 1024) /* 1 MBytes */ + +typedef struct { + gint byte_in_rate; // byte + gint byte_out_rate; // byte + gint time_rate; // ms +} streaming_bitrate_info_t; + +typedef struct { + gint64 position; // ns + gint64 duration; // ns + guint64 content_size; // bytes +} streaming_content_info_t; +typedef struct { + guint buffering_bytes; // bytes + gint buffering_time; // ms + gdouble low_watermark; + gdouble high_watermark; +} streaming_buffer_info_t; + +static void +__streaming_update_buffer_setting(mmplayer_streaming_t *streamer, + GstMessage *buffering_msg, + guint64 content_size, + gint64 position, + gint64 duration); -mm_player_streaming_t * -__mm_player_streaming_create () +mmplayer_streaming_t *_mm_player_streaming_create(void) { - mm_player_streaming_t *streamer = NULL; + mmplayer_streaming_t *streamer = NULL; - debug_fenter(); + MMPLAYER_FENTER(); - streamer = (mm_player_streaming_t *) malloc (sizeof (mm_player_streaming_t)); - if (!streamer) - { - debug_error ("fail to create streaming player handle..\n"); + streamer = (mmplayer_streaming_t *)g_try_malloc0(sizeof(mmplayer_streaming_t)); + if (!streamer) { + LOGE("fail to create streaming player handle.."); return NULL; } - debug_fleave(); + MMPLAYER_FLEAVE(); return streamer; } -void __mm_player_streaming_initialize (mm_player_streaming_t* streamer) +static void __streaming_buffer_initialize(streaming_buffer_t *buffer_handle, gboolean buffer_init) { - debug_fenter(); + if (buffer_init) + buffer_handle->buffer = NULL; + + buffer_handle->buffering_bytes = DEFAULT_BUFFER_SIZE_BYTES; + buffer_handle->buffering_time = DEFAULT_BUFFERING_TIME; + buffer_handle->buffer_high_watermark = DEFAULT_BUFFER_HIGH_WATERMARK; + buffer_handle->buffer_low_watermark = DEFAULT_BUFFER_LOW_WATERMARK; + buffer_handle->is_live = FALSE; +} + +void _mm_player_streaming_initialize(mmplayer_streaming_t *streamer, gboolean buffer_init) +{ + MMPLAYER_FENTER(); + MMPLAYER_RETURN_IF_FAIL(streamer); + + streamer->streaming_buffer_type = BUFFER_TYPE_DEFAULT; + + __streaming_buffer_initialize(&(streamer->buffer_handle[BUFFER_TYPE_MUXED]), buffer_init); + __streaming_buffer_initialize(&(streamer->buffer_handle[BUFFER_TYPE_DEMUXED]), buffer_init); + + streamer->buffering_req.mode = MM_PLAYER_BUFFERING_MODE_ADAPTIVE; + streamer->buffering_req.is_pre_buffering = TRUE; + streamer->buffering_req.prebuffer_time = 0; + streamer->buffering_req.rebuffer_time = 0; + + streamer->default_val.buffering_monitor = FALSE; + streamer->default_val.buffering_time = DEFAULT_BUFFERING_TIME; - streamer->buffer = NULL; - streamer->buffer_size = DEFAULT_BUFFER_SIZE; - streamer->buffer_low_percent = DEFAULT_BUFFER_LOW_PERCENT; - streamer->buffer_high_percent = DEFAULT_BUFFER_HIGH_PERCENT; streamer->buffer_avg_bitrate = 0; streamer->buffer_max_bitrate = 0; streamer->need_update = FALSE; + streamer->need_sync = FALSE; + + streamer->buffering_state = MM_PLAYER_BUFFERING_DEFAULT; + streamer->is_adaptive_streaming = FALSE; - streamer->is_buffering = FALSE; streamer->buffering_percent = -1; - streamer->buffering_time = DEFAULT_BUFFERING_TIME; + streamer->ring_buffer_size = DEFAULT_RING_BUFFER_SIZE_BYTES; + + MMPLAYER_FLEAVE(); + return; +} + +void _mm_player_streaming_destroy(mmplayer_streaming_t *streamer) +{ + MMPLAYER_FENTER(); - debug_fleave(); + MMPLAYER_FREEIF(streamer); + + MMPLAYER_FLEAVE(); return; } -void __mm_player_streaming_deinitialize (mm_player_streaming_t* streamer) +void _mm_player_streaming_set_content_bitrate( + mmplayer_streaming_t *streamer, guint max_bitrate, guint avg_bitrate) { - debug_fenter(); + gboolean is_update = FALSE; - return_if_fail(streamer); + MMPLAYER_FENTER(); + MMPLAYER_RETURN_IF_FAIL(streamer); - streamer->buffer_size = DEFAULT_BUFFER_SIZE; - streamer->buffer_low_percent = DEFAULT_BUFFER_LOW_PERCENT; - streamer->buffer_high_percent = DEFAULT_BUFFER_HIGH_PERCENT; - streamer->buffer_avg_bitrate = 0; - streamer->buffer_max_bitrate = 0; - streamer->need_update = FALSE; + if (max_bitrate > 0 && max_bitrate != streamer->buffer_max_bitrate) { + is_update = TRUE; + streamer->buffer_max_bitrate = max_bitrate; + } - streamer->is_buffering = FALSE; - streamer->buffering_percent = -1; - streamer->buffering_time = DEFAULT_BUFFERING_TIME; + if (avg_bitrate > 0 && avg_bitrate != streamer->buffer_avg_bitrate) { + is_update = TRUE; + streamer->buffer_avg_bitrate = avg_bitrate; + } - debug_fleave(); + if (is_update) { + if (streamer->buffering_req.is_pre_buffering) { + /* have to recalc queue2 size value after getting bitrate information */ + if (IS_MUXED_BUFFERING_MODE(streamer)) + __streaming_update_buffer_setting(streamer, NULL, 0, 0, 0); + } else { + streamer->need_update = TRUE; + } + } + MMPLAYER_FLEAVE(); return; } +static void __streaming_calc_watermark(mmplayer_streaming_t *streamer, + guint expected_play_bytes, gint expected_play_time, gdouble *low_wm, gdouble *high_wm) +{ +#define PORTION_OF_HIGH_WM 1.1 /* need to compensate about low level */ +#define PORTION_OF_LOW_WM 0.1 + + streaming_buffer_t *buffer_handle = NULL; + gdouble wm_byte = 0.0, wm_time = 0.0; + gint current_buffering_size = 0; + + MMPLAYER_RETURN_IF_FAIL(streamer && low_wm && high_wm); + + buffer_handle = &(streamer->buffer_handle[streamer->streaming_buffer_type]); + + LOGD("type %d, bytes %d / %d, time %d / %d", streamer->streaming_buffer_type, + expected_play_bytes, GET_CURRENT_BUFFERING_BYTE(buffer_handle), + expected_play_time, GET_CURRENT_BUFFERING_TIME(buffer_handle)); + + if (expected_play_bytes > 0) { + /* and buffer size will be increased */ + current_buffering_size = MAX(GET_CURRENT_BUFFERING_BYTE(buffer_handle), (expected_play_bytes * PORTION_OF_HIGH_WM)); + GET_WATERMARK((expected_play_bytes * PORTION_OF_HIGH_WM), current_buffering_size, buffer_handle->buffer_high_watermark, wm_byte); + wm_byte = MIN(wm_byte, DEFAULT_BUFFER_HIGH_WATERMARK); + } + + if (IS_MUXED_BUFFERING_MODE(streamer)) { + *high_wm = wm_byte; + *low_wm = wm_byte * PORTION_OF_LOW_WM; + + /* If user set the very low level buffering time, + buffer underrun could be occurred even if the min range is considered. */ + GET_VALID_VALUE((*low_wm), LOW_WATERMARK_MIN_BYTE_LEVEL, DEFAULT_BUFFER_LOW_WATERMARK); + } else { + gdouble prev_low_wm_time = 0.0; + gdouble low_wm_time = 0.0, high_wm_time = 0.0; + + expected_play_time = MAX(expected_play_time, LOW_WATERMARK_MIN_TIME_VALUE); + low_wm_time = expected_play_time * PORTION_OF_LOW_WM; + high_wm_time = expected_play_time * PORTION_OF_HIGH_WM; + + /* 1. Get min watermark value */ + /* 1-1. get max buffer size, expected time could be greater than current one */ + LOGE("low wm time : %.3f ms", low_wm_time); + current_buffering_size = MAX(GET_CURRENT_BUFFERING_TIME(buffer_handle), high_wm_time); + + /* 1-2. get previous low time value to keep the minimum watermark */ + prev_low_wm_time = GET_CURRENT_BUFFERING_TIME(buffer_handle) * buffer_handle->buffer_low_watermark; + GET_VALID_VALUE(low_wm_time, LOW_WATERMARK_MIN_TIME_VALUE, prev_low_wm_time); + + /* 1-3. get min watermark value based on the max buffer size */ + GET_WATERMARK(low_wm_time, current_buffering_size, buffer_handle->buffer_low_watermark, *low_wm); + *low_wm = MIN(*low_wm, DEFAULT_BUFFER_LOW_WATERMARK); + + /* 2. Get high watermark value. + If the expected time is greater than current one, the buffer size will be increased after */ + GET_WATERMARK(high_wm_time, current_buffering_size, buffer_handle->buffer_high_watermark, wm_time); + *high_wm = MAX(wm_byte, wm_time); + *high_wm = MIN(*high_wm, DEFAULT_BUFFER_HIGH_WATERMARK); + } + + if (*low_wm >= *high_wm) { + LOGW("invalid high wm value %1.3f", *high_wm); + *high_wm = DEFAULT_BUFFER_HIGH_WATERMARK; + } + + LOGD("new watermark value [%f ~ %f]", *low_wm, *high_wm); +} -void __mm_player_streaming_destroy (mm_player_streaming_t* streamer) +static void __streaming_set_buffer_watermark(mmplayer_streaming_t *streamer, + buffer_type_e type, gdouble low_watermark, gdouble high_watermark) { - debug_fenter(); + streaming_buffer_t *buffer_handle = NULL; + gchar *factory_name = NULL; - if(streamer) - { - free (streamer); - streamer = NULL; + MMPLAYER_FENTER(); + MMPLAYER_RETURN_IF_FAIL(streamer); + MMPLAYER_RETURN_IF_FAIL(type < BUFFER_TYPE_MAX); + + buffer_handle = &(streamer->buffer_handle[type]); + if (!(buffer_handle && buffer_handle->buffer)) { + LOGE("buffer_handle->buffer is NULL!"); + return; + } + + factory_name = GST_OBJECT_NAME(gst_element_get_factory(buffer_handle->buffer)); + + if (!factory_name) { + LOGE("Fail to get factory name!"); + return; } - debug_fleave(); + if (high_watermark <= MIN_BUFFER_WATERMARK || high_watermark >= MAX_BUFFER_WATERMARK) + high_watermark = DEFAULT_BUFFER_HIGH_WATERMARK; + + if (low_watermark <= MIN_BUFFER_WATERMARK || low_watermark >= MAX_BUFFER_WATERMARK) + low_watermark = DEFAULT_BUFFER_LOW_WATERMARK; + + /* if use-buffering is disabled, this settings do not have any meaning. */ + if ((high_watermark == DEFAULT_BUFFER_HIGH_WATERMARK) || + (buffer_handle->buffer_high_watermark != high_watermark) || + (buffer_handle->buffer_low_watermark != low_watermark)) + g_object_set(G_OBJECT(buffer_handle->buffer), "high-watermark", high_watermark, + "low-watermark", low_watermark, NULL); + + LOGD("update elem:%s, wm:%1.3f ~ %1.3f", GST_ELEMENT_NAME(buffer_handle->buffer), low_watermark, high_watermark); + + buffer_handle->buffer_high_watermark = high_watermark; + buffer_handle->buffer_low_watermark = low_watermark; + MMPLAYER_FLEAVE(); return; } - -void __mm_player_streaming_set_buffer(mm_player_streaming_t* streamer, GstElement * buffer, - gboolean use_buffering, guint buffer_size, gdouble low_percent, gdouble high_percent, gdouble buffering_time, - gboolean use_file, gchar * file_path, guint64 content_size) +static void __streaming_set_queue2_queue_type(mmplayer_streaming_t *streamer, muxed_buffer_type_e type) { - debug_fenter(); - - return_if_fail(streamer); + streaming_buffer_t *buffer_handle = NULL; + guint64 buffer_size = 0; /* bytes */ - if (buffer) - { - streamer->buffer = buffer; + MMPLAYER_FENTER(); + MMPLAYER_RETURN_IF_FAIL(streamer); + MMPLAYER_RETURN_IF_FAIL(streamer->buffer_handle[BUFFER_TYPE_MUXED].buffer); - debug_log("buffer element is %s.", GST_ELEMENT_NAME(buffer)); + buffer_handle = &(streamer->buffer_handle[BUFFER_TYPE_MUXED]); - g_object_set ( G_OBJECT (streamer->buffer), "use-buffering", use_buffering, NULL ); + if (type == MUXED_BUFFER_TYPE_MEM_QUEUE) { /* ts */ + LOGD("use memory queue for buffering. streaming is played on push-based. \n" + "buffering position would not be updated.\n" + "buffered data would be flushed after played.\n" + "seeking and getting duration could be failed due to file format."); + return; } - streaming_set_buffer_size(streamer, buffer_size); - streaming_set_buffer_percent(streamer, low_percent, high_percent); - streaming_set_buffer_type (streamer, use_file, file_path, content_size); - streaming_set_buffering_time(streamer, buffering_time); + /* @see ini->http_ring_buffer_size */ + buffer_size = (guint64)((streamer->ring_buffer_size > 0) ? (streamer->ring_buffer_size) : DEFAULT_RING_BUFFER_SIZE_BYTES); - debug_fleave(); + LOGW("ring buffer size: %"G_GUINT64_FORMAT, buffer_size); + g_object_set(G_OBJECT(buffer_handle->buffer), "ring-buffer-max-size", buffer_size, NULL); + MMPLAYER_FLEAVE(); return; } +static void __streaming_set_buffer_size(mmplayer_streaming_t *streamer, + buffer_type_e type, guint buffering_bytes, gint buffering_time) +{ + streaming_buffer_t *buffer_handle = NULL; + + MMPLAYER_FENTER(); + + MMPLAYER_RETURN_IF_FAIL(streamer); + MMPLAYER_RETURN_IF_FAIL(buffering_bytes > 0); + MMPLAYER_RETURN_IF_FAIL(type < BUFFER_TYPE_MAX); + + buffer_handle = &(streamer->buffer_handle[type]); + + if (buffer_handle && buffer_handle->buffer) { + if (g_strrstr(GST_ELEMENT_NAME(buffer_handle->buffer), "multiqueue")) { + if (buffering_time <= 0) + buffering_time = GET_CURRENT_BUFFERING_TIME(buffer_handle); + + g_object_set(G_OBJECT(buffer_handle->buffer), + "max-size-bytes", MAX_BUFFER_SIZE_BYTES, /* mq size is fixed, control it with high/low watermark value*/ + "max-size-time", (guint64)(buffering_time * GST_MSECOND), + "max-size-buffers", 0, /* disable */ + "use-interleave", FALSE, NULL); + + GET_CURRENT_BUFFERING_TIME(buffer_handle) = buffering_time; + GET_CURRENT_BUFFERING_BYTE(buffer_handle) = MAX_BUFFER_SIZE_BYTES; + + LOGD("byte: %d, max-size-time : %d ms", buffering_bytes, buffering_time); + } else { + /* queue2 */ + if (buffer_handle->is_live) + g_object_set(G_OBJECT(buffer_handle->buffer), + "max-size-bytes", buffering_bytes, + "max-size-time", (guint64)(buffering_time * GST_MSECOND), + "max-size-buffers", 0, + "use-rate-estimate", TRUE, NULL); + else + g_object_set(G_OBJECT(buffer_handle->buffer), + "max-size-bytes", buffering_bytes, + "max-size-time", (guint64)0, + "max-size-buffers", 0, + "use-rate-estimate", FALSE, NULL); + + GET_CURRENT_BUFFERING_BYTE(buffer_handle) = buffering_bytes; + GET_CURRENT_BUFFERING_TIME(buffer_handle) = buffering_time; + + LOGD("max-size-bytes : %d, time : %d", buffering_bytes, buffering_time); + } + } + + MMPLAYER_FLEAVE(); + return; +} -void __mm_player_streaming_set_content_bitrate(mm_player_streaming_t* streamer, guint max_bitrate, guint avg_bitrate) +void _mm_player_streaming_set_queue2(mmplayer_streaming_t *streamer, GstElement *buffer, + gboolean use_buffering, muxed_buffer_type_e type, guint64 content_size) { - debug_fenter(); + guint queue_size_bytes = 0; + guint queue_size_time = 0; - return_if_fail(streamer); + MMPLAYER_FENTER(); + MMPLAYER_RETURN_IF_FAIL(streamer && buffer); - /* Note : Update buffering criterion bytes - * 1. maximum bitrate is considered first. - * 2. average bitrage * 3 is next. - * 3. if there are no updated bitrate, use default buffering limit. - */ - if (max_bitrate > 0 && streamer->buffer_max_bitrate != max_bitrate) - { - debug_log("set maximum bitrate(%dbps).\n", max_bitrate); - streamer->buffer_max_bitrate = max_bitrate; + LOGD("use buffering %d, type %d, content_size %"G_GUINT64_FORMAT, use_buffering, type, content_size); - streamer->need_update = TRUE; - } + streamer->buffer_handle[BUFFER_TYPE_MUXED].buffer = buffer; - if (avg_bitrate > 0 && streamer->buffer_avg_bitrate != avg_bitrate) - { - debug_log("set averate bitrate(%dbps).\n", avg_bitrate); - streamer->buffer_avg_bitrate = avg_bitrate; + if (use_buffering) { /* audio only content use queue2 for buffering */ + streamer->streaming_buffer_type = BUFFER_TYPE_MUXED; - streamer->need_update = TRUE; + if (streamer->buffering_req.prebuffer_time <= MIN_BUFFERING_TIME) + streamer->buffering_req.prebuffer_time = DEFAULT_PREBUFFERING_TIME; + + if (content_size == 0) { + LOGD("live streaming without mq"); + streamer->buffer_handle[BUFFER_TYPE_MUXED].is_live = TRUE; + } + + /* keep estimated value till the pipeline share the bitrate information */ + queue_size_bytes = (guint)(streamer->buffering_req.prebuffer_time / 1000) * ESTIMATED_BUFFER_UNIT; /* temp size */ + queue_size_bytes = MAX(queue_size_bytes, DEFAULT_BUFFER_SIZE_BYTES); + queue_size_time = streamer->buffering_req.prebuffer_time; + } else { + if (type >= MUXED_BUFFER_TYPE_MAX) { + LOGE("invalid queue type"); + return; + } + + /* set the simple queue size */ + queue_size_bytes = DEFAULT_BUFFER_SIZE_BYTES; + __streaming_set_queue2_queue_type(streamer, type); + queue_size_time = 0; /* set in case of use-buffering */ } - debug_fleave(); + g_object_set(G_OBJECT(streamer->buffer_handle[BUFFER_TYPE_MUXED].buffer), "use-buffering", use_buffering, NULL); + LOGD("buffer time: %d ms, buffer bytes: %d", queue_size_time, queue_size_bytes); + __streaming_set_buffer_size(streamer, BUFFER_TYPE_MUXED, queue_size_bytes, queue_size_time); + __streaming_set_buffer_watermark(streamer, BUFFER_TYPE_MUXED, DEFAULT_BUFFER_LOW_WATERMARK, DEFAULT_BUFFER_HIGH_WATERMARK); + + MMPLAYER_FLEAVE(); return; } -static void -streaming_set_buffer_size(mm_player_streaming_t* streamer, guint buffer_size) +void _mm_player_streaming_sync_property(mmplayer_streaming_t *streamer, GstElement *decodebin) { - debug_fenter(); + streaming_buffer_t *buffer_handle = NULL; - return_if_fail(streamer); - return_if_fail(buffer_size>0); + MMPLAYER_FENTER(); + MMPLAYER_RETURN_IF_FAIL(streamer && decodebin); - debug_log("set buffer size to %d.", buffer_size); + if ((!streamer->need_sync) || (streamer->streaming_buffer_type != BUFFER_TYPE_DEMUXED)) { + streamer->need_sync = FALSE; + return; + } - streamer->buffer_size = buffer_size; + buffer_handle = &(streamer->buffer_handle[BUFFER_TYPE_DEMUXED]); - if (streamer->buffer) - g_object_set (G_OBJECT(streamer->buffer), "max-size-bytes", buffer_size, NULL); + LOGD("sync : %1.3f ~ %1.3f", buffer_handle->buffer_low_watermark, buffer_handle->buffer_high_watermark); + g_object_set(G_OBJECT(decodebin), + "max-size-bytes", GET_CURRENT_BUFFERING_BYTE(buffer_handle), + "max-size-time", (guint64)(GET_CURRENT_BUFFERING_TIME(buffer_handle) * GST_MSECOND), + "high-percent", (gint)(buffer_handle->buffer_high_watermark * 100), + "low-percent", (gint)(buffer_handle->buffer_low_watermark * 100), NULL); - debug_fleave(); + streamer->need_sync = FALSE; +} + +void _mm_player_streaming_set_multiqueue(mmplayer_streaming_t *streamer, GstElement *buffer) +{ + streaming_buffer_t *buffer_handle = NULL; + gdouble high_wm = 0.0, low_wm = 0.0; + MMPLAYER_FENTER(); + MMPLAYER_RETURN_IF_FAIL(streamer && buffer); + + buffer_handle = &(streamer->buffer_handle[BUFFER_TYPE_DEMUXED]); + buffer_handle->buffer = buffer; + + LOGD("pre_buffering: %d ms", streamer->buffering_req.prebuffer_time); + + streamer->streaming_buffer_type = BUFFER_TYPE_DEMUXED; + g_object_set(G_OBJECT(buffer_handle->buffer), "use-buffering", TRUE, NULL); + + if (streamer->buffering_req.prebuffer_time <= MIN_BUFFERING_TIME) + streamer->buffering_req.prebuffer_time = DEFAULT_PREBUFFERING_TIME; + + /* initial setting */ + __streaming_set_buffer_size(streamer, BUFFER_TYPE_DEMUXED, MAX_BUFFER_SIZE_BYTES, MAX_BUFFER_SIZE_TIME); + + __streaming_calc_watermark(streamer, 0, streamer->buffering_req.prebuffer_time, &low_wm, &high_wm); + __streaming_set_buffer_watermark(streamer, BUFFER_TYPE_DEMUXED, low_wm, high_wm); + + streamer->need_sync = TRUE; + + MMPLAYER_FLEAVE(); return; } -static void -streaming_set_buffer_percent(mm_player_streaming_t* streamer, gdouble low_percent, gdouble high_percent) +static void __streaming_get_current_bitrate_info(mmplayer_streaming_t *streamer, + GstMessage *buffering_msg, streaming_content_info_t content_info, streaming_bitrate_info_t *bitrate_info) { - gdouble buffer_low_percent = DEFAULT_BUFFER_LOW_PERCENT; - gdouble buffer_high_percent = DEFAULT_BUFFER_HIGH_PERCENT; + GstQuery *query = NULL; + GstBufferingMode mode = GST_BUFFERING_STREAM; + gint in_rate = 0; + gint out_rate = 0; + gint64 buffering_left = -1; - debug_fenter(); + guint estimated_content_bitrate = 0; - return_if_fail(streamer); + gint buffer_buffering_time = DEFAULT_BUFFERING_TIME; - if (low_percent <= MIN_BUFFER_PERCENT || low_percent >= MAX_BUFFER_PERCENT) - { - debug_warning("buffer low percent is out of range. use defaut value."); - buffer_low_percent = DEFAULT_BUFFER_LOW_PERCENT; - } - else - { - buffer_low_percent = low_percent; - } + MMPLAYER_FENTER(); - if (high_percent <= MIN_BUFFER_PERCENT || high_percent >= MAX_BUFFER_PERCENT) - { - debug_warning("buffer high percent is out of range. use defaut value."); - buffer_high_percent = DEFAULT_BUFFER_HIGH_PERCENT; - } - else - { - buffer_high_percent = high_percent; - } + MMPLAYER_RETURN_IF_FAIL(streamer); + MMPLAYER_RETURN_IF_FAIL(bitrate_info); - if (buffer_high_percent <= buffer_low_percent) - buffer_high_percent = buffer_low_percent + 1.0; + if ((buffering_msg == NULL) || + ((streamer->buffer_handle[BUFFER_TYPE_DEMUXED].buffer != NULL) && + (streamer->buffer_handle[BUFFER_TYPE_MUXED].buffer != NULL) && + (buffering_msg->src == (GstObject *)streamer->buffer_handle[BUFFER_TYPE_DEMUXED].buffer))) { + query = gst_query_new_buffering(GST_FORMAT_PERCENT); + + if (gst_element_query((streamer->buffer_handle[BUFFER_TYPE_MUXED].buffer), query)) + gst_query_parse_buffering_stats(query, &mode, &in_rate, &out_rate, &buffering_left); + gst_query_unref(query); + } else { + gst_message_parse_buffering_stats(buffering_msg, &mode, &in_rate, &out_rate, &buffering_left); + } - debug_log("set buffer percent to %2.3f ~ %2.3f.", streamer->buffer_low_percent, streamer->buffer_high_percent); + LOGD("Streaming Info : in %d, out %d, left %"G_GINT64_FORMAT, in_rate, out_rate, buffering_left); - if (streamer->buffer) - { - if ( streamer->buffer_low_percent != buffer_low_percent ) - g_object_set (G_OBJECT(streamer->buffer), "low-percent", streamer->buffer_low_percent, NULL); + if ((content_info.content_size > 0) && (content_info.duration > 0) && ((content_info.duration / GST_SECOND) > 0)) + estimated_content_bitrate = GET_BIT_FROM_BYTE((guint)(content_info.content_size / (content_info.duration / GST_SECOND))); - if ( streamer->buffer_high_percent != buffer_high_percent ) - g_object_set (G_OBJECT(streamer->buffer), "high-percent", streamer->buffer_high_percent, NULL); + if (streamer->buffer_max_bitrate > 0) { + streamer->buffer_max_bitrate = MAX(streamer->buffer_max_bitrate, streamer->buffer_avg_bitrate); + streamer->buffer_max_bitrate = MAX(streamer->buffer_max_bitrate, estimated_content_bitrate); } - streamer->buffer_low_percent = buffer_low_percent; - streamer->buffer_high_percent = buffer_high_percent; + if (streamer->buffer_avg_bitrate > 0) + out_rate = GET_BYTE_FROM_BIT(MAX(streamer->buffer_avg_bitrate, estimated_content_bitrate)); + else if (estimated_content_bitrate > 0) + out_rate = GET_BYTE_FROM_BIT(estimated_content_bitrate); + else if (streamer->buffer_max_bitrate > 0) + out_rate = GET_BYTE_FROM_BIT(streamer->buffer_max_bitrate/3); + else + LOGW("There is no content information"); - debug_fleave(); + LOGD("byte_out_rate %d", out_rate); - return; + if ((in_rate > 0) && (out_rate > 0)) + buffer_buffering_time = (gint)(out_rate / in_rate) * 1000; + else if ((in_rate <= 0) && (out_rate > 0)) + buffer_buffering_time = MAX_BUFFERING_TIME; + else + buffer_buffering_time = DEFAULT_BUFFERING_TIME; + + (*bitrate_info).byte_in_rate = in_rate; + (*bitrate_info).byte_out_rate = out_rate; + (*bitrate_info).time_rate = buffer_buffering_time; } -static void -streaming_set_buffering_time(mm_player_streaming_t* streamer, gdouble buffering_time) +static void __streaming_handle_fixed_buffering_mode(mmplayer_streaming_t *streamer, + gint byte_out_rate, gint expected_play_time, streaming_buffer_info_t *buffer_info) { - gdouble buffer_buffering_time = DEFAULT_BUFFERING_TIME; + streaming_buffer_t *buffer_handle = NULL; - debug_fenter(); + guint expected_play_bytes = 0; + gdouble high_wm = 0.0, low_wm = 0.0; - return_if_fail(streamer); + MMPLAYER_RETURN_IF_FAIL(streamer); + MMPLAYER_RETURN_IF_FAIL(buffer_info); - if (buffering_time < MIN_BUFFERING_TIME) - buffer_buffering_time = MIN_BUFFERING_TIME; - else if (buffering_time > MAX_BUFFERING_TIME) - buffer_buffering_time = MAX_BUFFERING_TIME; - else - buffer_buffering_time = buffering_time; + buffer_handle = &(streamer->buffer_handle[streamer->streaming_buffer_type]); - if (streamer->buffering_time != buffer_buffering_time) - { - debug_log("set buffer buffering time from %2.1f to %2.1f.", streamer->buffering_time, buffer_buffering_time); + LOGD("buffering time: %d ms, out rate: %d", expected_play_time, byte_out_rate); - streamer->buffering_time = buffer_buffering_time; + if (byte_out_rate > 0) { + guint max_size_byte = GET_MAX_BUFFER_SIZE_BYTES(streamer->streaming_buffer_type); + expected_play_bytes = (guint)GET_NEW_BUFFERING_BYTE((gdouble)(byte_out_rate * expected_play_time) / 1000, max_size_byte); + } else { + LOGW("content bitrate is not updated yet."); + expected_play_bytes = GET_CURRENT_BUFFERING_BYTE(buffer_handle); } - debug_fleave(); + __streaming_calc_watermark(streamer, expected_play_bytes, expected_play_time, &low_wm, &high_wm); - return; + (*buffer_info).buffering_bytes = expected_play_bytes; + (*buffer_info).buffering_time = expected_play_time; + (*buffer_info).high_watermark = high_wm; + (*buffer_info).low_watermark = low_wm; } -static void -streaming_set_buffer_type (mm_player_streaming_t* streamer, gboolean use_file, gchar * file_path, guint64 content_size) +static void __streaming_handle_adaptive_buffering_mode(mmplayer_streaming_t *streamer, + streaming_content_info_t content_info, streaming_bitrate_info_t bitrate_info, + streaming_buffer_info_t *buffer_info, gint expected_play_time) { - guint64 storage_available_size = 0L; //bytes - guint64 file_buffer_size = 0L; //bytes - gchar file_buffer_name[MAX_FILE_BUFFER_NAME_LEN] = {0}; - struct statfs buf = {0}; + gint buffering_bytes = 0; + gint adj_buffering_bytes = 0; + gint buffer_buffering_time = 0; + gdouble high_wm = 0.0, low_wm = 0.0; + gdouble portion = 0.0; + gint default_buffering_time = 0; + + MMPLAYER_RETURN_IF_FAIL(streamer); + MMPLAYER_RETURN_IF_FAIL(buffer_info); + + LOGD("monitor %d, pos %"G_GINT64_FORMAT", dur %"G_GINT64_FORMAT", size %"G_GUINT64_FORMAT", in/out:%d/%d, time_rate:%d, need:%d ms", + streamer->default_val.buffering_monitor, content_info.position, content_info.duration, content_info.content_size, + bitrate_info.byte_in_rate, bitrate_info.byte_out_rate, bitrate_info.time_rate, expected_play_time); + + if ((content_info.duration <= 0) || + (content_info.content_size <= 0)) { + LOGW("keep previous setting."); + return; + } + + if (bitrate_info.byte_out_rate <= 0) { + LOGW("keep previous setting."); + return; + } - debug_fenter(); + if (bitrate_info.byte_in_rate < bitrate_info.byte_out_rate) { + guint max_size_byte = GET_MAX_BUFFER_SIZE_BYTES(streamer->streaming_buffer_type); + portion = (double)(expected_play_time * GST_MSECOND) / (double)content_info.duration; + buffering_bytes = GET_NEW_BUFFERING_BYTE( + (((double)content_info.content_size * portion) * + (1 - (double)bitrate_info.byte_in_rate / (double)bitrate_info.byte_out_rate)), max_size_byte); + } else { + /* buffering_bytes will be set as streamer->default_val.buffering_time * + * receiving rate is bigger than avg content bitrate + * so there is no reason to buffering. if the buffering msg is posted + * in-rate or contents bitrate has wrong value. */ + LOGW("don't need to do buffering."); + } - return_if_fail(streamer && streamer->buffer); + if (buffering_bytes > 0) + buffer_buffering_time = (gint)(buffering_bytes / bitrate_info.byte_out_rate) * 1000; - if (!use_file) - { - debug_log("use memory for buffering. streaming is played on push-based. \n" - "buffering position would not be updated.\n" - "buffered data would be flushed after played.\n" - "seeking and getting duration could be failed due to file format."); - return; + if (content_info.position <= 0) { + /* if the buffer is filled under 50%, MSL use the original default buffering time. + if not, MSL use just 2 sec as a default buffering time. (to reduce initial buffering time) */ + default_buffering_time = streamer->default_val.buffering_time - ((gdouble)streamer->buffering_percent / 50) * 1000; + } else { + default_buffering_time = streamer->default_val.buffering_time; } - debug_log("use file for buffering. streaming is played on pull-based. \n"); + if (buffer_buffering_time < default_buffering_time) { + guint max_size_byte = GET_MAX_BUFFER_SIZE_BYTES(streamer->streaming_buffer_type); - if (!file_path || strlen(file_path) <= 0) - file_path = g_strdup(DEFAULT_FILE_BUFFER_PATH); + LOGD("adjusted time: %d -> %d ms", buffer_buffering_time, default_buffering_time); + LOGD("adjusted bytes : %d or %d", buffering_bytes, + (gint)(bitrate_info.byte_out_rate * buffer_buffering_time / 1000)); - sprintf( file_buffer_name, "%s/XXXXXX", file_path ); - debug_log("the buffering file name is %s.\n", file_buffer_name); + /* start monitoring the abmormal state */ + if (content_info.position > 0) + streamer->default_val.buffering_monitor = TRUE; - if (statfs((const char *)file_path, &buf) < 0) - { - debug_warning ("fail to get availabe storage capacity. just use file buffer.\n"); - file_buffer_size = 0L; + buffer_buffering_time = default_buffering_time; + adj_buffering_bytes = GET_NEW_BUFFERING_BYTE( + (bitrate_info.byte_out_rate * (gint)ceil((gdouble)buffer_buffering_time / 1000)), max_size_byte); + buffering_bytes = MAX(buffering_bytes, adj_buffering_bytes); } - else - { - storage_available_size = (guint64)buf.f_bavail * (guint64)buf.f_bsize; //bytes - debug_log ("the number of available blocks : %"G_GUINT64_FORMAT", the block size is %"G_GUINT64_FORMAT".\n", - (guint64)buf.f_bavail, (guint64)buf.f_bsize); - debug_log ("calculated availabe storage size is %"G_GUINT64_FORMAT" Bytes.\n", storage_available_size); + __streaming_calc_watermark(streamer, buffering_bytes, buffer_buffering_time, &low_wm, &high_wm); + + (*buffer_info).buffering_bytes = buffering_bytes; + (*buffer_info).buffering_time = buffer_buffering_time; + (*buffer_info).high_watermark = high_wm; + (*buffer_info).low_watermark = low_wm; +} - if (content_size <= 0 || content_size >= storage_available_size) - file_buffer_size = storage_available_size; +static void __streaming_update_buffer_setting(mmplayer_streaming_t *streamer, + GstMessage *buffering_msg, guint64 content_size, gint64 position, gint64 duration) +{ + streaming_buffer_t *buffer_handle = NULL; + mmplayer_buffering_mode_e buffering_mode = MM_PLAYER_BUFFERING_MODE_ADAPTIVE; + gint expected_play_time = DEFAULT_REBUFFERING_TIME; + + streaming_buffer_info_t buffer_info = {0,}; + streaming_content_info_t content_info = {0,}; + streaming_bitrate_info_t bitrate_info = {0,}; + + MMPLAYER_FENTER(); + MMPLAYER_RETURN_IF_FAIL(streamer); + + buffer_handle = &(streamer->buffer_handle[streamer->streaming_buffer_type]); + + if (streamer->buffering_req.is_pre_buffering == TRUE) { + buffering_mode = MM_PLAYER_BUFFERING_MODE_FIXED; + expected_play_time = streamer->buffering_req.prebuffer_time; + } else { + if (content_size == 0 || duration == 0) /* can not support adaptive mode */ + buffering_mode = MM_PLAYER_BUFFERING_MODE_FIXED; else - file_buffer_size = 0L; + buffering_mode = streamer->buffering_req.mode; + + if (buffering_mode == MM_PLAYER_BUFFERING_MODE_FIXED) { + if (streamer->buffering_req.rebuffer_time > 0) + expected_play_time = streamer->buffering_req.rebuffer_time; + else + expected_play_time = DEFAULT_LIVE_REBUFFER_TIME; + } } - if (file_buffer_size>0) - debug_log("use file ring buffer for buffering."); + LOGD("buffering mode: %d, time: %d", buffering_mode, expected_play_time); - g_object_set (G_OBJECT(streamer->buffer), "temp-template", file_buffer_name, NULL); - g_object_set (G_OBJECT(streamer->buffer), "file-buffer-max-size", file_buffer_size, NULL); + content_info.position = position; + content_info.duration = duration; + content_info.content_size = content_size; - debug_fleave(); + __streaming_get_current_bitrate_info(streamer, buffering_msg, content_info, &bitrate_info); - return; + LOGD("in_r:%d, out_r:%d", bitrate_info.byte_in_rate, bitrate_info.byte_out_rate); + + if (buffering_mode == MM_PLAYER_BUFFERING_MODE_FIXED) { + __streaming_handle_fixed_buffering_mode(streamer, bitrate_info.byte_out_rate, expected_play_time, &buffer_info); + } else { + __streaming_handle_adaptive_buffering_mode(streamer, content_info, bitrate_info, &buffer_info, expected_play_time); + + /* even if new byte size is smaller than the previous one, time need to be updated. */ + if (IS_MUXED_BUFFERING_MODE(streamer)) + GET_CURRENT_BUFFERING_TIME(buffer_handle) = buffer_info.buffering_time; + } + + LOGD("adj buffer(%d) %d->%d bytes/%d->%d ms", + streamer->streaming_buffer_type, + GET_CURRENT_BUFFERING_BYTE(buffer_handle), buffer_info.buffering_bytes, + GET_CURRENT_BUFFERING_TIME(buffer_handle), buffer_info.buffering_time); + + /* queue2 : bytes, multiqueue : time */ + if (((GET_CURRENT_BUFFERING_BYTE(buffer_handle) < buffer_info.buffering_bytes) && IS_MUXED_BUFFERING_MODE(streamer)) || + ((GET_CURRENT_BUFFERING_TIME(buffer_handle) < buffer_info.buffering_time) && IS_DEMUXED_BUFFERING_MODE(streamer))) { + if (duration > 0 && position > 0) { + gint buffering_time_limit = (gint)(duration - position) / GST_MSECOND; + + if (buffer_info.buffering_time > buffering_time_limit) + buffer_info.buffering_time = buffering_time_limit; + } + __streaming_set_buffer_size(streamer, streamer->streaming_buffer_type, buffer_info.buffering_bytes, buffer_info.buffering_time); + } + + __streaming_set_buffer_watermark(streamer, streamer->streaming_buffer_type, buffer_info.low_watermark, buffer_info.high_watermark); + + LOGD("buffer setting: size %d, time %d, watermark %f", GET_CURRENT_BUFFERING_BYTE(buffer_handle), + GET_CURRENT_BUFFERING_TIME(buffer_handle), buffer_handle->buffer_high_watermark); + + streamer->need_sync = TRUE; } -#define GET_BYTE_FROM_BIT(bit) (bit/8) -void __mm_player_streaming_buffering(mm_player_streaming_t* streamer, GstMessage *buffering_msg) +static void __streaming_adjust_min_threshold(mmplayer_streaming_t *streamer, gint64 position) { - GstBufferingMode mode = GST_BUFFERING_STREAM; - gint byte_in_rate = 0; - gint byte_out_rate = 0; - gint64 buffering_left = -1; - gdouble buffering_time = DEFAULT_BUFFERING_TIME; - gdouble low_percent = 0.0; - gdouble high_percent = 0.0; - guint high_percent_byte = 0; - gint buffer_percent = 0; - guint buffer_criteria = 0; +#define DEFAULT_TIME_PAD 1000 /* ms */ + gint playing_time = 0; - return_if_fail ( streamer ); - return_if_fail ( buffering_msg ); - return_if_fail ( GST_IS_MESSAGE ( buffering_msg ) ); - return_if_fail ( GST_MESSAGE_TYPE ( buffering_msg ) == GST_MESSAGE_BUFFERING ); + MMPLAYER_FENTER(); - /* update when buffering has started. */ - if ( !streamer->is_buffering ) - { - debug_log ( "buffering has started.\n" ); + MMPLAYER_RETURN_IF_FAIL(streamer); - streamer->is_buffering = TRUE; - streamer->buffering_percent = -1; - streamer->need_update = TRUE; - } + playing_time = (gint)((position - streamer->default_val.prev_pos) / GST_MSECOND); - /* update buffer percent */ - gst_message_parse_buffering ( buffering_msg, &buffer_percent ); + LOGD("buffering monitor = %s", (streamer->default_val.buffering_monitor) ? "ON" : "OFF"); + LOGD("playing_time (%d ms) = %"G_GINT64_FORMAT" - %"G_GINT64_FORMAT, playing_time, position, streamer->default_val.prev_pos); + LOGD("default time : %d, prev buffering t : %d", + streamer->default_val.buffering_time, streamer->buffer_handle[streamer->streaming_buffer_type].buffering_time); - if ( streamer->buffering_percent < buffer_percent ) - { - debug_log ( "buffering %d%%....\n", buffer_percent ); - streamer->buffering_percent = buffer_percent; - } + if ((streamer->default_val.buffering_monitor) && (playing_time <= streamer->default_val.buffering_time)) { + gint time_gap = 0; + time_gap = streamer->default_val.buffering_time - DEFAULT_BUFFERING_TIME; + if (time_gap <= 0) + time_gap = DEFAULT_TIME_PAD; - if ( streamer->buffering_percent == MAX_BUFFER_PERCENT ) - { - debug_log ( "buffering had done.\n" ); - streamer->is_buffering = FALSE; + streamer->default_val.buffering_time += time_gap * 2; + streamer->default_val.buffering_time = MIN(streamer->default_val.buffering_time, MAX_BUFFERING_TIME); + } else { + streamer->default_val.buffering_time = DEFAULT_BUFFERING_TIME; } - if (!streamer->need_update) - { - debug_log ( "don't need to update buffering stats during buffering.\n" ); - return; - } + LOGD("new default min value %d", streamer->default_val.buffering_time); - /* Note : Parse the buffering message to get the in/out throughput. - * avg_in is the network throughput and avg_out is the consumed throughtput by the linkded element. - */ - gst_message_parse_buffering_stats ( buffering_msg, &mode, &byte_in_rate, &byte_out_rate, &buffering_left ); + streamer->default_val.buffering_monitor = FALSE; + streamer->default_val.prev_pos = position; +} - if (streamer->buffer_max_bitrate > 0) - { - buffer_criteria = GET_BYTE_FROM_BIT(streamer->buffer_max_bitrate); - byte_out_rate = GET_BYTE_FROM_BIT(streamer->buffer_max_bitrate /3); - } - else if (streamer->buffer_avg_bitrate > 0) - { - buffer_criteria = GET_BYTE_FROM_BIT(streamer->buffer_avg_bitrate * 3); - byte_out_rate = GET_BYTE_FROM_BIT(streamer->buffer_avg_bitrate); - } +static void __streaming_update_buffering_status(mmplayer_streaming_t *streamer, GstMessage *buffering_msg, gint64 position) +{ + gint buffer_percent = 0; - debug_log ( "in rate is %d, out rate is %d (bytes/sec).\n", byte_in_rate, byte_out_rate ); + MMPLAYER_FENTER(); - if ( byte_in_rate > 0 && byte_out_rate > 0) - buffering_time = byte_out_rate / byte_in_rate; - else if (byte_in_rate <= 0 && byte_out_rate > 0) - buffering_time = MAX_BUFFERING_TIME; - else - buffering_time = DEFAULT_BUFFERING_TIME; + MMPLAYER_RETURN_IF_FAIL(streamer); + MMPLAYER_RETURN_IF_FAIL(buffering_msg); + MMPLAYER_RETURN_IF_FAIL(GST_IS_MESSAGE(buffering_msg)); + MMPLAYER_RETURN_IF_FAIL((GST_MESSAGE_TYPE(buffering_msg) == GST_MESSAGE_BUFFERING)); + + /* update when buffering has started. */ + if (!(streamer->buffering_state & MM_PLAYER_BUFFERING_IN_PROGRESS)) { + streamer->buffering_state = MM_PLAYER_BUFFERING_IN_PROGRESS; + streamer->buffering_percent = -1; - streaming_set_buffering_time(streamer, buffering_time); + if (!streamer->buffering_req.is_pre_buffering) { + streamer->need_update = TRUE; + __streaming_adjust_min_threshold(streamer, position); + } + } - /* calculate buffer low/high percent */ - low_percent = DEFAULT_BUFFER_LOW_PERCENT; + /* update buffer percent */ + gst_message_parse_buffering(buffering_msg, &buffer_percent); - if ( buffer_criteria > 0 ) - { - high_percent_byte = buffer_criteria * streamer->buffering_time; - high_percent = ( (gdouble)high_percent_byte * 100.0 ) / (gdouble)streamer->buffer_size; + /* LOGD("[%s] buffering per %d%% -> %d%%, state 0x%X", streamer->buffering_percent, buffer_percent, streamer->buffering_state); */ + + if (streamer->buffering_percent < buffer_percent) { + LOGD("[%s] buffering %d%%....", + GST_OBJECT_NAME(GST_MESSAGE_SRC(buffering_msg)), buffer_percent); + streamer->buffering_percent = buffer_percent; } - else - { - high_percent_byte = streamer->buffer_high_percent * streamer->buffer_size / 100; - high_percent= streamer->buffer_high_percent; + + if (streamer->buffering_percent == MAX_BUFFER_PERCENT) { + streamer->buffering_state = MM_PLAYER_BUFFERING_DEFAULT; + streamer->buffering_req.is_pre_buffering = FALSE; + streamer->need_update = TRUE; + } else if (streamer->buffering_state & MM_PLAYER_BUFFERING_COMPLETE) { + streamer->buffering_state = MM_PLAYER_BUFFERING_COMPLETE; /* have to keep state to ignore remained msg till get 100% msg */ + streamer->buffering_req.is_pre_buffering = FALSE; + streamer->buffering_percent = MAX_BUFFER_PERCENT; + LOGD("updated to the buffering 100%%.... (by force)"); } +} - if ( streamer->buffer_size < high_percent_byte ) - { - debug_log ( "buffer size[%d bytes] is smaller than high threshold[%d bytes]. update it. \n", - streamer->buffer_size, high_percent_byte ); +void _mm_player_streaming_buffering(mmplayer_streaming_t *streamer, GstMessage *buffering_msg, + guint64 content_size, gint64 position, gint64 duration) +{ + MMPLAYER_FENTER(); + MMPLAYER_RETURN_IF_FAIL(streamer); - streaming_set_buffer_size(streamer, high_percent_byte * 1.1); - } + if (buffering_msg) { + if (position > (gint64)(streamer->buffering_req.prebuffer_time * GST_MSECOND)) + streamer->buffering_req.is_pre_buffering = FALSE; + + __streaming_update_buffering_status(streamer, buffering_msg, position); - streaming_set_buffer_percent(streamer, low_percent, high_percent); + if (!streamer->need_update) + return; + } streamer->need_update = FALSE; + __streaming_update_buffer_setting(streamer, buffering_msg, content_size, position, duration); return; } -