[0.6.185] send pcm_format info to client
[platform/core/multimedia/libmm-player.git] / src / mm_player_priv.c
index 644c8e1..df00ea4 100644 (file)
 
 #define FAKE_SINK_MAX_LATENESS         G_GINT64_CONSTANT(20000000) /* set 20ms as waylandsink */
 
+#define DEFAULT_PCM_OUT_FORMAT         "F32LE"
+#define DEFAULT_PCM_OUT_SAMPLERATE     44100
+#define DEFAULT_PCM_OUT_CHANNEL        2
+
 /*---------------------------------------------------------------------------
 |    LOCAL CONSTANT DEFINITIONS:                                                                                       |
 ---------------------------------------------------------------------------*/
@@ -701,17 +705,11 @@ __mmplayer_gst_remove_fakesink(mmplayer_t *player, mmplayer_gst_element_t *fakes
        GstElement *parent = NULL;
 
        MMPLAYER_RETURN_VAL_IF_FAIL(player && player->pipeline, FALSE);
-
-       /* if we have no fakesink. this meas we are using decodebin which doesn'
-       t need to add extra fakesink */
-       MMPLAYER_RETURN_VAL_IF_FAIL(fakesink, TRUE);
+       MMPLAYER_RETURN_VAL_IF_FAIL(fakesink && fakesink->gst, TRUE);
 
        /* lock */
        MMPLAYER_FSINK_LOCK(player);
 
-       if (!fakesink->gst)
-               goto ERROR;
-
        /* get parent of fakesink */
        parent = (GstElement *)gst_object_get_parent((GstObject *)fakesink->gst);
        if (!parent) {
@@ -1119,7 +1117,7 @@ __mmplayer_gst_decode_pad_added(GstElement *elem, GstPad *pad, gpointer data)
 
                /* in case of exporting video frame, it requires the 360 video filter.
                 * it will be handled in _no_more_pads(). */
-               if ((stype == MM_DISPLAY_SURFACE_NULL) && (!player->set_mode.media_packet_video_stream)) {
+               if ((stype == MM_DISPLAY_SURFACE_NULL) && (!player->set_mode.video_export)) {
                        __mmplayer_gst_make_fakesink(player, pad, name);
                        goto DONE;
                }
@@ -1525,6 +1523,12 @@ __mmplayer_gst_create_sinkbin(GstElement *elem, GstPad *pad, gpointer data)
        /* LOGD("detected mimetype : %s", name); */
        if (strstr(name, "audio")) {
                if (player->pipeline->audiobin == NULL) {
+                       const gchar *audio_format = gst_structure_get_string(str, "format");
+                       if (audio_format) {
+                               LOGD("original audio format %s", audio_format);
+                               mm_attrs_set_string_by_name(player->attrs, "content_audio_format", audio_format);
+                       }
+
                        if (__mmplayer_gst_create_audio_sink_bin(player) != MM_ERROR_NONE) {
                                LOGE("failed to create audiobin. continuing without audio");
                                goto ERROR;
@@ -2103,6 +2107,7 @@ __mmplayer_gst_element_link_bucket(GList *element_bucket)
        GList *bucket = element_bucket;
        mmplayer_gst_element_t *element = NULL;
        mmplayer_gst_element_t *prv_element = NULL;
+       GstElement *tee_element = NULL;
        gint successful_link_count = 0;
 
        MMPLAYER_FENTER();
@@ -2117,11 +2122,25 @@ __mmplayer_gst_element_link_bucket(GList *element_bucket)
 
                if (element && element->gst) {
                        if (prv_element && prv_element->gst) {
+                               if (strstr(GST_ELEMENT_NAME(element->gst), "audio-tee-queue") && strcmp(GST_ELEMENT_NAME(prv_element->gst), "audio-tee")) {
+                                       if (tee_element) {
+                                               prv_element->gst = tee_element;
+                                       } else {
+                                               LOGD("failed to make new audio branch - linking [%s] to [%s] is not supported",
+                                                       GST_ELEMENT_NAME(GST_ELEMENT(prv_element->gst)),
+                                                       GST_ELEMENT_NAME(GST_ELEMENT(element->gst)));
+                                               return -1;
+                                       }
+                               }
                                if (gst_element_link(GST_ELEMENT(prv_element->gst), GST_ELEMENT(element->gst))) {
                                        LOGD("linking [%s] to [%s] success",
                                                GST_ELEMENT_NAME(GST_ELEMENT(prv_element->gst)),
                                                GST_ELEMENT_NAME(GST_ELEMENT(element->gst)));
                                        successful_link_count++;
+                                       if (!strcmp(GST_ELEMENT_NAME(prv_element->gst), "audio-tee")) {
+                                               LOGD("keep audio-tee element for next audio pipeline branch");
+                                               tee_element = prv_element->gst;
+                                       }
                                } else {
                                        LOGD("linking [%s] to [%s] failed",
                                                GST_ELEMENT_NAME(GST_ELEMENT(prv_element->gst)),
@@ -2225,31 +2244,6 @@ ERROR:
        return;
 }
 
-/**
- * This function is to create audio pipeline for playing.
- *
- * @param      player          [in]    handle of player
- *
- * @return     This function returns zero on success.
- * @remark
- * @see                __mmplayer_gst_create_midi_pipeline, __mmplayer_gst_create_video_sink_bin
- */
-/* macro for code readability. just for sinkbin-creation functions */
-#define MMPLAYER_CREATE_ELEMENT(x_bin, x_id, x_factory, x_name, x_add_bucket, x_player) \
-       do {\
-               x_bin[x_id].id = x_id;\
-               x_bin[x_id].gst = gst_element_factory_make(x_factory, x_name);\
-               if (!x_bin[x_id].gst) {\
-                       LOGE("failed to create %s", x_factory);\
-                       goto ERROR;\
-               } else {\
-                       if (x_player->ini.set_dump_element_flag)\
-                               __mmplayer_add_dump_buffer_probe(x_player, x_bin[x_id].gst);\
-               } \
-               if (x_add_bucket)\
-                       element_bucket = g_list_append(element_bucket, &x_bin[x_id]);\
-       } while (0);
-
 void
 __mmplayer_audio_stream_clear_buffer(mmplayer_t *player, gboolean send_all)
 {
@@ -2283,7 +2277,7 @@ __mmplayer_audio_stream_send_data(mmplayer_t *player, mmplayer_audio_stream_buff
        mmplayer_audio_decoded_data_info_t audio_stream = { 0, };
 
        MMPLAYER_FENTER();
-       MMPLAYER_RETURN_IF_FAIL(player && player->audio_stream_render_cb);
+       MMPLAYER_RETURN_IF_FAIL(player && player->audio_decoded_cb);
 
        audio_stream.bitrate = a_buffer->bitrate;
        audio_stream.channel = a_buffer->channel;
@@ -2292,9 +2286,10 @@ __mmplayer_audio_stream_send_data(mmplayer_t *player, mmplayer_audio_stream_buff
        audio_stream.channel_mask = a_buffer->channel_mask;
        audio_stream.data_size = a_buffer->data_size;
        audio_stream.data = a_buffer->pcm_data;
+       audio_stream.pcm_format = a_buffer->pcm_format;
 
-       /* LOGD("[%"G_GUINT64_FORMAT"] send data size:%d, %p", audio_stream.channel_mask, audio_stream.data_size, player->audio_stream_cb_user_param); */
-       player->audio_stream_render_cb(&audio_stream, player->audio_stream_cb_user_param);
+       /* LOGD("[%"G_GUINT64_FORMAT"] send data size:%d, %p", audio_stream.channel_mask, audio_stream.data_size, player->audio_decoded_cb_user_param); */
+       player->audio_decoded_cb(&audio_stream, player->audio_decoded_cb_user_param);
 
        MMPLAYER_FLEAVE();
 }
@@ -2303,6 +2298,7 @@ static void
 __mmplayer_audio_stream_decoded_render_cb(GstElement *object, GstBuffer *buffer, GstPad *pad, gpointer data)
 {
        mmplayer_t *player = (mmplayer_t *)data;
+       const gchar *pcm_format = NULL;
        gint channel = 0;
        gint rate = 0;
        gint depth = 0;
@@ -2315,7 +2311,7 @@ __mmplayer_audio_stream_decoded_render_cb(GstElement *object, GstBuffer *buffer,
        GList *l = NULL;
 
        MMPLAYER_FENTER();
-       MMPLAYER_RETURN_IF_FAIL(player && player->audio_stream_render_cb);
+       MMPLAYER_RETURN_IF_FAIL(player && player->audio_decoded_cb);
 
        gst_buffer_map(buffer, &mapinfo, GST_MAP_READ);
        a_data = mapinfo.data;
@@ -2325,6 +2321,7 @@ __mmplayer_audio_stream_decoded_render_cb(GstElement *object, GstBuffer *buffer,
        GstStructure *structure = gst_caps_get_structure(caps, 0);
 
        /* MMPLAYER_LOG_GST_CAPS_TYPE(caps); */
+       pcm_format = gst_structure_get_string(structure, "format");
        gst_structure_get_int(structure, "rate", &rate);
        gst_structure_get_int(structure, "channels", &channel);
        gst_structure_get_int(structure, "depth", &depth);
@@ -2369,7 +2366,7 @@ __mmplayer_audio_stream_decoded_render_cb(GstElement *object, GstBuffer *buffer,
                }
        }
 
-       /* create new audio stream data */
+       /* create new audio stream data for newly found audio channel */
        a_buffer = (mmplayer_audio_stream_buff_t *)g_try_malloc0(sizeof(mmplayer_audio_stream_buff_t));
        if (a_buffer == NULL) {
                LOGE("failed to alloc data.");
@@ -2381,8 +2378,9 @@ __mmplayer_audio_stream_decoded_render_cb(GstElement *object, GstBuffer *buffer,
        a_buffer->is_little_endian = (endianness == 1234 ? true : false);
        a_buffer->channel_mask = channel_mask;
        a_buffer->data_size = a_size;
+       a_buffer->pcm_format = util_convert_audio_pcm_str_to_media_format_mime(pcm_format);
 
-       if (!player->audio_stream_sink_sync) {
+       if (player->audio_extract_opt & MM_PLAYER_AUDIO_EXTRACT_NO_SYNC_WITH_CLOCK) {
                /* If sync is FALSE, use buffer list to reduce the IPC. */
                a_buffer->buff_size = (a_size > player->ini.pcm_buffer_size) ? (a_size) : (player->ini.pcm_buffer_size);
                a_buffer->pcm_data = g_try_malloc(a_buffer->buff_size);
@@ -2443,12 +2441,19 @@ __mmplayer_gst_audio_deinterleave_pad_added(GstElement *elem, GstPad *pad, gpoin
                goto ERROR;
        }
 
-       LOGE("player->audio_stream_sink_sync: %d", player->audio_stream_sink_sync);
+       LOGE("audio_extract_opt : 0x%X", player->audio_extract_opt);
 
        gst_object_unref(sinkpad);
-       g_object_set(sink, "sync", player->audio_stream_sink_sync, NULL);
+       if (!(player->audio_extract_opt & MM_PLAYER_AUDIO_EXTRACT_NO_SYNC_WITH_CLOCK))
+               g_object_set(sink, "sync", TRUE, NULL);
        g_object_set(sink, "signal-handoffs", TRUE, NULL);
 
+       /* keep the first sink reference only */
+       if (!audiobin[MMPLAYER_A_SINK].gst) {
+               audiobin[MMPLAYER_A_SINK].id = MMPLAYER_A_SINK;
+               audiobin[MMPLAYER_A_SINK].gst = sink;
+       }
+
        gst_element_set_state(sink, GST_STATE_PAUSED);
        gst_element_set_state(queue, GST_STATE_PAUSED);
 
@@ -2459,6 +2464,8 @@ __mmplayer_gst_audio_deinterleave_pad_added(GstElement *elem, GstPad *pad, gpoin
                G_CALLBACK(__mmplayer_audio_stream_decoded_render_cb),
                (gpointer)player);
 
+       __mmplayer_add_sink(player, sink);
+
        MMPLAYER_FLEAVE();
        return;
 
@@ -2481,7 +2488,7 @@ ERROR:
 }
 
 void
-__mmplayer_gst_set_pulsesink_property(mmplayer_t *player, MMHandleType attrs)
+__mmplayer_gst_set_pulsesink_property(mmplayer_t *player)
 {
        #define MAX_PROPS_LEN 128
        gint latency_mode = 0;
@@ -2498,26 +2505,21 @@ __mmplayer_gst_set_pulsesink_property(mmplayer_t *player, MMHandleType attrs)
        MMPLAYER_FENTER();
        MMPLAYER_RETURN_IF_FAIL(player && player->pipeline && player->pipeline->audiobin);
 
-       mm_attrs_get_int_by_name(attrs, "sound_stream_index", &stream_id);
-       mm_attrs_get_string_by_name(attrs, "sound_stream_type", &stream_type);
+       mm_attrs_get_int_by_name(player->attrs, "sound_stream_index", &stream_id);
+       mm_attrs_get_string_by_name(player->attrs, "sound_stream_type", &stream_type);
 
        if (!stream_type) {
                LOGE("stream_type is null.");
        } else {
-               if (player->sound.focus_id)
-                       snprintf(stream_props, sizeof(stream_props) - 1, "props,media.role=%s, media.parent_id=%d, media.focus_id=%d",
-                                       stream_type, stream_id, player->sound.focus_id);
-               else
-                       snprintf(stream_props, sizeof(stream_props) - 1, "props,media.role=%s, media.parent_id=%d",
-                                       stream_type, stream_id);
+               snprintf(stream_props, sizeof(stream_props) - 1, "props,media.role=%s, media.parent_id=%d",
+                               stream_type, stream_id);
                props = gst_structure_from_string(stream_props, NULL);
                g_object_set(player->pipeline->audiobin[MMPLAYER_A_SINK].gst, "stream-properties", props, NULL);
-               LOGI("stream_type[%s], stream_id[%d], focus_id[%d], result[%s].",
-                       stream_type, stream_id, player->sound.focus_id, stream_props);
+               LOGI("stream_type[%s], stream_id[%d], result[%s].", stream_type, stream_id, stream_props);
                gst_structure_free(props);
        }
 
-       mm_attrs_get_int_by_name(attrs, "sound_latency_mode", &latency_mode);
+       mm_attrs_get_int_by_name(player->attrs, "sound_latency_mode", &latency_mode);
 
        switch (latency_mode) {
        case AUDIO_LATENCY_MODE_LOW:
@@ -2573,13 +2575,12 @@ __mmplayer_gst_set_openalsink_property(mmplayer_t *player)
 }
 
 static int
-__mmplayer_gst_fill_audio_bucket(mmplayer_t *player, GList **bucket)
+__mmplayer_gst_make_audio_playback_sink(mmplayer_t *player, GList **bucket)
 {
        mmplayer_gst_element_t *audiobin = NULL;
-       MMHandleType attrs = 0;
-       GList *element_bucket = NULL;
-       GstCaps *acaps = NULL;
        GstPad *sink_pad = NULL;
+       GstCaps *acaps = NULL;
+       gint channels = 0;
        int pitch_control = 0;
        double pitch_value = 1.0;
 
@@ -2588,17 +2589,17 @@ __mmplayer_gst_fill_audio_bucket(mmplayer_t *player, GList **bucket)
                                player->pipeline->audiobin, MM_ERROR_PLAYER_NOT_INITIALIZED);
 
        audiobin = player->pipeline->audiobin;
-       attrs = MMPLAYER_GET_ATTRS(player);
 
-       if (player->build_audio_offload) { /* skip all the audio filters */
-               LOGD("create audio offload sink : %s", player->ini.audio_offload_sink_element);
-               MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_SINK, player->ini.audio_offload_sink_element, "audiosink", TRUE, player);
-               g_object_set(G_OBJECT(audiobin[MMPLAYER_A_SINK].gst), "sync", TRUE, NULL);
-               __mmplayer_add_sink(player, audiobin[MMPLAYER_A_SINK].gst);
-               goto DONE;
-       }
+       LOGD("make element for normal audio playback");
+
+       /* audio bin structure for playback. {} means optional.
+          optional : pitch, audioeq, custom audioeq, openalsink for 360 audio content
 
-       /* pitch */
+        * src - ... - {aconv - pitch} - aconv - rgvolume - resample - volume -
+                       {audioeq} - {custom audioeq} - pulsesink or {aconv - capsfilter - openalsink}
+        */
+
+       /* for pitch control */
        mm_attrs_multiple_get(player->attrs, NULL,
                                MM_PLAYER_PITCH_CONTROL, &pitch_control,
                                MM_PLAYER_PITCH_VALUE, &pitch_value,
@@ -2613,10 +2614,10 @@ __mmplayer_gst_fill_audio_bucket(mmplayer_t *player, GList **bucket)
                        gst_object_unref(factory);
 
                        /* converter */
-                       MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_CONV_PITCH, "audioconvert", "audio convert pitch", TRUE, player);
+                       MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_CONV_PITCH, "audioconvert", "audio convert pitch", *bucket, player);
 
                        /* pitch */
-                       MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_PITCH, "pitch", "audio pitch", TRUE, player);
+                       MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_PITCH, "pitch", "audio pitch", *bucket, player);
                        g_object_set(G_OBJECT(audiobin[MMPLAYER_A_PITCH].gst), "pitch", (gdouble)pitch_value, NULL);
                } else {
                        LOGW("there is no pitch element");
@@ -2624,158 +2625,307 @@ __mmplayer_gst_fill_audio_bucket(mmplayer_t *player, GList **bucket)
        }
 
        /* converter */
-       MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_CONV, "audioconvert", "audio converter", TRUE, player);
+       MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_CONV, "audioconvert", "audio converter", *bucket, player);
 
        /* replaygain volume */
-       MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_RGVOL, "rgvolume", "audio rgvolume", TRUE, player);
+       MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_RGVOL, "rgvolume", "audio rgvolume", *bucket, player);
        if (player->sound.rg_enable)
                g_object_set(G_OBJECT(audiobin[MMPLAYER_A_RGVOL].gst), "enable-rgvolume", TRUE, NULL);
        else
                g_object_set(G_OBJECT(audiobin[MMPLAYER_A_RGVOL].gst), "enable-rgvolume", FALSE, NULL);
 
        /* resampler */
-       MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_RESAMPLER,  player->ini.audioresampler_element, "audio resampler", TRUE, player);
+       MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_RESAMPLER,  player->ini.audioresampler_element, "audio resampler", *bucket, player);
 
-       if (player->audio_stream_render_cb) { /* pcm extraction only, no sound output */
-               gchar *dst_format = NULL;
-               int dst_len = 0;
-               int dst_samplerate = 0;
-               int dst_channels = 0;
-               GstCaps *caps = NULL;
-               char *caps_str = NULL;
+       /* for logical volume control */
+       MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_VOL, "volume", "volume", *bucket, player);
+       g_object_set(G_OBJECT(audiobin[MMPLAYER_A_VOL].gst), "volume", player->sound.volume, NULL);
 
-               /* get conf. values */
-               mm_attrs_multiple_get(player->attrs, NULL,
-                                       "pcm_audioformat", &dst_format, &dst_len,
-                                       "pcm_extraction_samplerate", &dst_samplerate,
-                                       "pcm_extraction_channels", &dst_channels,
-                                       NULL);
+       if (player->sound.mute) {
+               LOGD("mute enabled");
+               g_object_set(G_OBJECT(audiobin[MMPLAYER_A_VOL].gst), "mute", player->sound.mute, NULL);
+       }
 
-               LOGD("pcm info - format: %s(%d), samplerate : %d, channel: %d", dst_format, dst_len, dst_samplerate, dst_channels);
+       mm_attrs_get_int_by_name(player->attrs, "content_audio_channels", &channels);
 
-               /* capsfilter */
-               MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_CAPS_DEFAULT, "capsfilter", "audio capsfilter", TRUE, player);
-               caps = gst_caps_new_simple("audio/x-raw",
-                               "format", G_TYPE_STRING, dst_format,
-                               "rate", G_TYPE_INT, dst_samplerate,
-                               "channels", G_TYPE_INT, dst_channels,
-                               NULL);
+       /* audio effect element. if audio effect is enabled */
+       if ((strcmp(player->ini.audioeffect_element, ""))
+               && (channels <= 2)
+               && (player->ini.use_audio_effect_preset || player->ini.use_audio_effect_custom)) {
+               MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_FILTER, player->ini.audioeffect_element, "audio effect filter", *bucket, player);
 
-               caps_str = gst_caps_to_string(caps);
-               LOGD("new caps : %s", caps_str);
+               LOGD("audio effect config. bypass = %d, effect type = %d", player->bypass_audio_effect, player->audio_effect_info.effect_type);
 
-               g_object_set(GST_ELEMENT(audiobin[MMPLAYER_A_CAPS_DEFAULT].gst), "caps", caps, NULL);
+               if ((!player->bypass_audio_effect)
+                       && (player->ini.use_audio_effect_preset || player->ini.use_audio_effect_custom)) {
+                       if (player->audio_effect_info.effect_type == MM_AUDIO_EFFECT_TYPE_CUSTOM) {
+                               if (!_mmplayer_audio_effect_custom_apply(player))
+                                       LOGI("apply audio effect(custom) setting success");
+                       }
+               }
 
-               /* clean */
-               gst_caps_unref(caps);
-               MMPLAYER_FREEIF(caps_str);
+               if ((strcmp(player->ini.audioeffect_element_custom, ""))
+                       && (player->set_mode.rich_audio)) {
+                       MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_FILTER_SEC, player->ini.audioeffect_element_custom, "audio effect filter custom", *bucket, player);
+               }
+       }
 
-               MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_DEINTERLEAVE, "deinterleave", "deinterleave", TRUE, player);
+       /* create audio sink */
+       LOGD("spherical %d, channels %d, ambisonic type %d, format %d, order %d",
+                       player->is_content_spherical, channels, player->video360_metadata.ambisonic_type,
+                       player->video360_metadata.ambisonic_format, player->video360_metadata.ambisonic_order);
 
-               g_object_set(G_OBJECT(audiobin[MMPLAYER_A_DEINTERLEAVE].gst), "keep-positions", TRUE, NULL);
+       /* Note: qtdemux converts audio metadata defaults to openalsink defaults. */
+       if (player->is_360_feature_enabled &&
+               player->is_content_spherical &&
+               channels == 4 &&
+               player->video360_metadata.ambisonic_type == MMFILE_AMBISONIC_TYPE_PERIPHONIC &&
+               player->video360_metadata.ambisonic_format == MMFILE_AMBISONIC_FORMAT_AMB &&
+               player->video360_metadata.ambisonic_order == MMFILE_AMBISONIC_ORDER_FOA) {
 
-               /* raw pad handling signal, audiosink will be added after getting signal */
-               __mmplayer_add_signal_connection(player, G_OBJECT(audiobin[MMPLAYER_A_DEINTERLEAVE].gst),
-                               MM_PLAYER_SIGNAL_TYPE_AUTOPLUG, "pad-added", G_CALLBACK(__mmplayer_gst_audio_deinterleave_pad_added), (gpointer)player);
+               strncpy(player->ini.audiosink_element, "openalsink", PLAYER_INI_MAX_STRLEN - 1);
+
+               MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_CONV_BFORMAT, "audioconvert", "audio-converter-bformat", *bucket, player);
+
+               MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_CAPS_360, "capsfilter", "audio-caps-filter", *bucket, player);
+               acaps = gst_caps_from_string(SPATIAL_AUDIO_CAPS);
+               g_object_set(G_OBJECT(audiobin[MMPLAYER_A_CAPS_360].gst), "caps", acaps, NULL);
+               gst_caps_unref(acaps);
 
+               MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_SINK, "openalsink", "audiosink", *bucket, player);
+
+               player->is_openal_plugin_used = TRUE;
        } else {
+               if (player->is_360_feature_enabled && player->is_content_spherical)
+                       LOGW("Audio track isn't of the ambisonic type and can't be played back as a spatial sound.");
+               MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_SINK, player->ini.audiosink_element, "audiosink", *bucket, player);
+       }
 
-               /* normal playback */
-               gint channels = 0;
+       if ((MMPLAYER_IS_RTSP_STREAMING(player)) ||
+               (player->videodec_linked && player->ini.use_system_clock)) {
+               LOGD("system clock will be used.");
+               g_object_set(G_OBJECT(audiobin[MMPLAYER_A_SINK].gst), "provide-clock", FALSE,  NULL);
+       }
 
-               /* for logical volume control */
-               MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_VOL, "volume", "volume", TRUE, player);
-               g_object_set(G_OBJECT(audiobin[MMPLAYER_A_VOL].gst), "volume", player->sound.volume, NULL);
+       if (g_strrstr(player->ini.audiosink_element, "pulsesink"))
+               __mmplayer_gst_set_pulsesink_property(player);
+       else if (g_strrstr(player->ini.audiosink_element, "openalsink"))
+               __mmplayer_gst_set_openalsink_property(player);
 
-               if (player->sound.mute) {
-                       LOGD("mute enabled");
-                       g_object_set(G_OBJECT(audiobin[MMPLAYER_A_VOL].gst), "mute", player->sound.mute, NULL);
-               }
+       /* qos on */
+       g_object_set(G_OBJECT(audiobin[MMPLAYER_A_SINK].gst), "qos", TRUE, NULL);       /* qos on */
+       g_object_set(G_OBJECT(audiobin[MMPLAYER_A_SINK].gst), "slave-method", GST_AUDIO_BASE_SINK_SLAVE_NONE, NULL);
 
-               mm_attrs_get_int_by_name(player->attrs, "content_audio_channels", &channels);
+       sink_pad = gst_element_get_static_pad(audiobin[MMPLAYER_A_SINK].gst, "sink");
+       __mmplayer_add_signal_connection(player, G_OBJECT(sink_pad), MM_PLAYER_SIGNAL_TYPE_AUDIOBIN,
+                               "notify::caps", G_CALLBACK(__mmplayer_gst_caps_notify_cb), (gpointer)player);
+       gst_object_unref(GST_OBJECT(sink_pad));
 
-               /* audio effect element. if audio effect is enabled */
-               if ((strcmp(player->ini.audioeffect_element, ""))
-                       && (channels <= 2)
-                       && (player->ini.use_audio_effect_preset || player->ini.use_audio_effect_custom)) {
-                       MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_FILTER, player->ini.audioeffect_element, "audio effect filter", TRUE, player);
+       __mmplayer_add_sink(player, audiobin[MMPLAYER_A_SINK].gst);
 
-                       LOGD("audio effect config. bypass = %d, effect type  = %d", player->bypass_audio_effect, player->audio_effect_info.effect_type);
+       MMPLAYER_FLEAVE();
+       return MM_ERROR_NONE;
 
-                       if ((!player->bypass_audio_effect)
-                               && (player->ini.use_audio_effect_preset || player->ini.use_audio_effect_custom)) {
-                               if (player->audio_effect_info.effect_type == MM_AUDIO_EFFECT_TYPE_CUSTOM) {
-                                       if (!_mmplayer_audio_effect_custom_apply(player))
-                                               LOGI("apply audio effect(custom) setting success");
-                               }
-                       }
+ERROR: /* MMPLAYER_CREATE_ELEMENT */
+       MMPLAYER_FLEAVE();
+       return MM_ERROR_PLAYER_INTERNAL;
+}
 
-                       if ((strcmp(player->ini.audioeffect_element_custom, ""))
-                               && (player->set_mode.rich_audio))
-                               MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_FILTER_SEC, player->ini.audioeffect_element_custom, "audio effect filter custom", TRUE, player);
-               }
+static int
+__mmplayer_gst_make_audio_extract_sink(mmplayer_t *player, GList **bucket)
+{
+       mmplayer_gst_element_t *audiobin = NULL;
+       enum audio_element_id extract_sink_id = MMPLAYER_A_SINK;
 
-               /* create audio sink */
-               LOGD("360 spherical %d, channels %d, ambisonic type %d, format %d, order %d",
-                               player->is_content_spherical, channels, player->video360_metadata.ambisonic_type,
-                               player->video360_metadata.ambisonic_format, player->video360_metadata.ambisonic_order);
+       gchar *dst_format = NULL;
+       int dst_len = 0;
+       int dst_samplerate = 0;
+       int dst_channels = 0;
+       GstCaps *caps = NULL;
+       char *caps_str = NULL;
 
-               /* Note: qtdemux converts audio metadata defaults to openalsink defaults. */
-               if (player->is_360_feature_enabled &&
-                       player->is_content_spherical &&
-                       channels == 4 &&
-                       player->video360_metadata.ambisonic_type == MMFILE_AMBISONIC_TYPE_PERIPHONIC &&
-                       player->video360_metadata.ambisonic_format == MMFILE_AMBISONIC_FORMAT_AMB &&
-                       player->video360_metadata.ambisonic_order == MMFILE_AMBISONIC_ORDER_FOA) {
+       MMPLAYER_FENTER();
+       MMPLAYER_RETURN_VAL_IF_FAIL(player && player->pipeline &&
+                               player->pipeline->audiobin, MM_ERROR_PLAYER_NOT_INITIALIZED);
 
-                       strncpy(player->ini.audiosink_element, "openalsink", PLAYER_INI_MAX_STRLEN - 1);
+       audiobin = player->pipeline->audiobin;
 
-                       MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_CONV_BFORMAT, "audioconvert", "audio-converter-bformat", TRUE, player);
+       LOGD("make element for audio extract, option = 0x%X", player->audio_extract_opt);
 
-                       MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_CAPS_360, "capsfilter", "audio-caps-filter", TRUE, player);
-                       acaps = gst_caps_from_string(SPATIAL_AUDIO_CAPS);
-                       g_object_set(G_OBJECT(audiobin[MMPLAYER_A_CAPS_360].gst), "caps", acaps, NULL);
-                       gst_caps_unref(acaps);
+       /* audio bin structure according to the mmplayer_audio_extract_opt_e.
 
-                       MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_SINK, "openalsink", "audiosink", TRUE, player);
+          [case 1] extract interleave audio pcm without playback
+                               : MM_PLAYER_AUDIO_EXTRACT_DEFAULT (sync)
+                                 MM_PLAYER_AUDIO_EXTRACT_NO_SYNC_WITH_CLOCK (non sync)
 
-                       player->is_openal_plugin_used = TRUE;
-               } else {
-                       if (player->is_360_feature_enabled && player->is_content_spherical)
-                               LOGW("Audio track isn't of the ambisonic type and can't be played back as a spatial sound.");
-                       MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_SINK, player->ini.audiosink_element, "audiosink", TRUE, player);
-               }
+                               * src - ... - aconv - resample - capsfilter - fakesink (sync or not)
+
+          [case 2] deinterleave for each channel without playback
+                               : MM_PLAYER_AUDIO_EXTRACT_DEINTERLEAVE (sync)
+                                 MM_PLAYER_AUDIO_EXTRACT_NO_SYNC_AND_DEINTERLEAVE (non sync)
+
+                               * src - ... - aconv - resample - capsfilter - deinterleave - fakesink (sync or not)
+                                                                                                                                                  - fakesink (sync or not)
+                                                                                                                                                  - ...      (sync or not)
+
+          [case 3] [case 1(sync only)] + playback
+                               : MM_PLAYER_AUDIO_EXTRACT_WITH_PLAYBACK
+
+                               * src - ... - tee - queue1 - playback path
+                                                                 - queue2 - [case1 pipeline with sync]
+
+          [case 4] [case 2(sync only)] + playback
+                               : MM_PLAYER_AUDIO_EXTRACT_DEINTERLEAVE_WITH_PLAYBACK
+
+                               * src - ... - tee - queue1 - playback path
+                                                                 - queue2 - [case2 pipeline with sync]
 
-               if ((MMPLAYER_IS_RTSP_STREAMING(player)) ||
-                       (player->videodec_linked && player->ini.use_system_clock)) {
-                       LOGD("system clock will be used.");
-                       g_object_set(G_OBJECT(audiobin[MMPLAYER_A_SINK].gst), "provide-clock", FALSE,  NULL);
+        */
+
+       /* 1. create tee and playback path
+             'tee' should be added at first to copy the decoded stream
+        */
+       if (player->audio_extract_opt & MM_PLAYER_AUDIO_EXTRACT_WITH_PLAYBACK) {
+               MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_TEE, "tee", "audio-tee", *bucket, player);
+               g_object_set(G_OBJECT(audiobin[MMPLAYER_A_TEE].gst), "num-src-pads", 2, NULL);
+
+               /* tee - path 1 : for playback path */
+               MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_TEE_Q1, "queue", "audio-tee-queue1", *bucket, player);
+               __mmplayer_gst_make_audio_playback_sink(player, bucket);
+
+               /* tee - path 2 : for extract path */
+               MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_TEE_Q2, "queue", "audio-tee-queue2", *bucket, player);
+               extract_sink_id = MMPLAYER_A_EXTRACT_SINK; /* there is another playback sink */
+       }
+
+       /* if there is tee, 'tee - path 2' is linked here */
+       /* converter */
+       MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_EXTRACT_CONV, "audioconvert", "audio-ext-conv", *bucket, player);
+
+       /* resampler */
+       MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_EXTRACT_RESAMPLER,  player->ini.audioresampler_element, "audio-ext-resampler", *bucket, player);
+
+       /* 2. decide the extract pcm format */
+       mm_attrs_multiple_get(player->attrs, NULL,
+                               "pcm_audioformat", &dst_format, &dst_len,
+                               "pcm_extraction_samplerate", &dst_samplerate,
+                               "pcm_extraction_channels", &dst_channels,
+                               NULL);
+
+       LOGD("required extract pcm format - format: %s(%d), samplerate : %d, channel: %d",
+                       dst_format, dst_len, dst_samplerate, dst_channels);
+
+       if (dst_format == NULL || dst_len == 0 || dst_samplerate == 0 || dst_channels == 0) {
+               mm_attrs_multiple_get(player->attrs, NULL,
+                                       "content_audio_format", &dst_format, &dst_len, /* get string and len */
+                                       "content_audio_samplerate", &dst_samplerate,
+                                       "content_audio_channels", &dst_channels,
+                                       NULL);
+
+               LOGD("apply the decoded pcm format - format: %s(%d), samplerate : %d, channel: %d",
+                               dst_format, dst_len, dst_samplerate, dst_channels);
+
+               /* If there is no enough information, set it to platform default value. */
+               if (dst_format == NULL || util_convert_audio_pcm_str_to_media_format_mime(dst_format) == MEDIA_FORMAT_MAX) {
+                       LOGD("set platform default format");
+                       dst_format = DEFAULT_PCM_OUT_FORMAT;
                }
+               if (dst_samplerate <= 0) dst_samplerate = DEFAULT_PCM_OUT_SAMPLERATE;
+               if (dst_channels <= 0)   dst_channels = DEFAULT_PCM_OUT_CHANNEL;
+       }
 
-               if (g_strrstr(player->ini.audiosink_element, "pulsesink"))
-                       __mmplayer_gst_set_pulsesink_property(player, attrs);
-               else if (g_strrstr(player->ini.audiosink_element, "openalsink"))
-                       __mmplayer_gst_set_openalsink_property(player);
+       /* 3. create capsfilter */
+       MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_EXTRACT_CAPS, "capsfilter", "audio-ext-caps", *bucket, player);
+       caps = gst_caps_new_simple("audio/x-raw",
+                       "format", G_TYPE_STRING, dst_format,
+                       "rate", G_TYPE_INT, dst_samplerate,
+                       "channels", G_TYPE_INT, dst_channels,
+                       NULL);
 
-               /* qos on */
-               g_object_set(G_OBJECT(audiobin[MMPLAYER_A_SINK].gst), "qos", TRUE, NULL);       /* qos on */
-               g_object_set(G_OBJECT(audiobin[MMPLAYER_A_SINK].gst), "slave-method", GST_AUDIO_BASE_SINK_SLAVE_NONE, NULL);
+       caps_str = gst_caps_to_string(caps);
+       LOGD("new caps : %s", caps_str);
 
-               sink_pad = gst_element_get_static_pad(audiobin[MMPLAYER_A_SINK].gst, "sink");
-               __mmplayer_add_signal_connection(player, G_OBJECT(sink_pad), MM_PLAYER_SIGNAL_TYPE_AUDIOBIN,
-                                       "notify::caps", G_CALLBACK(__mmplayer_gst_caps_notify_cb), (gpointer)player);
-               gst_object_unref(GST_OBJECT(sink_pad));
+       g_object_set(GST_ELEMENT(audiobin[MMPLAYER_A_EXTRACT_CAPS].gst), "caps", caps, NULL);
+
+       /* clean */
+       gst_caps_unref(caps);
+       MMPLAYER_FREEIF(caps_str);
+
+       /* 4-1. create deinterleave to extract pcm for each channel */
+       if (player->audio_extract_opt & MM_PLAYER_AUDIO_EXTRACT_DEINTERLEAVE) {
+               MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_EXTRACT_DEINTERLEAVE, "deinterleave", "deinterleave", *bucket, player);
+               g_object_set(G_OBJECT(audiobin[MMPLAYER_A_EXTRACT_DEINTERLEAVE].gst), "keep-positions", TRUE, NULL);
+
+               /* audiosink will be added after getting signal for each channel */
+               __mmplayer_add_signal_connection(player, G_OBJECT(audiobin[MMPLAYER_A_EXTRACT_DEINTERLEAVE].gst),
+                               MM_PLAYER_SIGNAL_TYPE_AUTOPLUG, "pad-added", G_CALLBACK(__mmplayer_gst_audio_deinterleave_pad_added), (gpointer)player);
+       } else {
+       /* 4-2. create fakesink to extract interlevaed pcm */
+               LOGD("add audio fakesink for interleaved audio");
+               MMPLAYER_CREATE_ELEMENT(audiobin, extract_sink_id, "fakesink", "fakeaudiosink", *bucket, player);
+               if (!(player->audio_extract_opt & MM_PLAYER_AUDIO_EXTRACT_NO_SYNC_WITH_CLOCK))
+                       g_object_set(G_OBJECT(audiobin[extract_sink_id].gst), "sync", TRUE, NULL);
+               g_object_set(G_OBJECT(audiobin[extract_sink_id].gst), "signal-handoffs", TRUE, NULL);
+
+               __mmplayer_add_signal_connection(player,
+                       G_OBJECT(audiobin[extract_sink_id].gst),
+                       MM_PLAYER_SIGNAL_TYPE_AUDIOBIN,
+                       "handoff",
+                       G_CALLBACK(__mmplayer_audio_stream_decoded_render_cb),
+                       (gpointer)player);
+
+               __mmplayer_add_sink(player, audiobin[extract_sink_id].gst);
+       }
+
+       MMPLAYER_FLEAVE();
+       return MM_ERROR_NONE;
+
+ERROR: /* MMPLAYER_CREATE_ELEMENT */
+       MMPLAYER_FLEAVE();
+       return MM_ERROR_PLAYER_INTERNAL;
+}
+
+static int
+__mmplayer_gst_make_audio_bin_element(mmplayer_t *player, GList **bucket)
+{
+       int ret = MM_ERROR_NONE;
+       mmplayer_gst_element_t *audiobin = NULL;
+       GList *element_bucket = NULL;
+
+       MMPLAYER_FENTER();
+       MMPLAYER_RETURN_VAL_IF_FAIL(player && player->pipeline &&
+                               player->pipeline->audiobin, MM_ERROR_PLAYER_NOT_INITIALIZED);
+
+       audiobin = player->pipeline->audiobin;
+
+       if (player->build_audio_offload) { /* skip all the audio filters */
+               LOGD("create audio offload sink : %s", player->ini.audio_offload_sink_element);
+
+               MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_SINK, player->ini.audio_offload_sink_element, "audiosink", element_bucket, player);
+               g_object_set(G_OBJECT(audiobin[MMPLAYER_A_SINK].gst), "sync", TRUE,
+                               "volume", player->sound.volume, "mute", player->sound.mute, NULL);
 
                __mmplayer_add_sink(player, audiobin[MMPLAYER_A_SINK].gst);
+               goto DONE;
        }
 
+       /* FIXME: need to mention the supportable condition at API reference */
+       if (player->audio_decoded_cb && (!MMPLAYER_IS_RTSP_STREAMING(player)))
+               ret = __mmplayer_gst_make_audio_extract_sink(player, &element_bucket);
+       else
+               ret = __mmplayer_gst_make_audio_playback_sink(player, &element_bucket);
+
+       if (ret != MM_ERROR_NONE)
+               goto ERROR;
 DONE:
+       LOGD("success to make audio bin element");
        *bucket = element_bucket;
 
        MMPLAYER_FLEAVE();
        return MM_ERROR_NONE;
 
 ERROR:
+       LOGE("failed to make audio bin element");
        g_list_free(element_bucket);
 
        *bucket = NULL;
@@ -2815,7 +2965,7 @@ __mmplayer_gst_create_audio_sink_bin(mmplayer_t *player)
        player->pipeline->audiobin = audiobin;
 
        /* create audio filters and audiosink */
-       if (__mmplayer_gst_fill_audio_bucket(player, &element_bucket) != MM_ERROR_NONE)
+       if (__mmplayer_gst_make_audio_bin_element(player, &element_bucket) != MM_ERROR_NONE)
                goto ERROR;
 
        /* adding created elements to bin */
@@ -3054,7 +3204,7 @@ __mmplayer_video_stream_decoded_preroll_cb(GstElement *object, GstBuffer *buffer
 {
        mmplayer_t *player = (mmplayer_t *)data;
        MMPLAYER_FENTER();
-       MMPLAYER_RETURN_IF_FAIL(player && player->video_stream_cb);
+       MMPLAYER_RETURN_IF_FAIL(player && player->video_decoded_cb);
 
        /* send prerolled pkt */
        player->video_stream_prerolled = false;
@@ -3074,7 +3224,7 @@ __mmplayer_video_stream_decoded_render_cb(GstElement *object, GstBuffer *buffer,
 
        MMPLAYER_FENTER();
        MMPLAYER_RETURN_IF_FAIL(player);
-       MMPLAYER_RETURN_IF_FAIL(player->video_stream_cb);
+       MMPLAYER_RETURN_IF_FAIL(player->video_decoded_cb);
 
        if (player->video_stream_prerolled) {
                player->video_stream_prerolled = false;
@@ -3098,7 +3248,7 @@ __mmplayer_video_stream_decoded_render_cb(GstElement *object, GstBuffer *buffer,
 
        /* check zero-copy */
        if (player->set_mode.video_zc &&
-               player->set_mode.media_packet_video_stream &&
+               player->set_mode.video_export &&
                gst_is_tizen_memory(mem)) {
                __mmplayer_zerocopy_set_stride_elevation_bo(stream, mem);
                stream->internal_buffer = gst_buffer_ref(buffer);
@@ -3110,8 +3260,8 @@ __mmplayer_video_stream_decoded_render_cb(GstElement *object, GstBuffer *buffer,
                        goto ERROR;
        }
 
-       if (!player->video_stream_cb(stream, player->video_stream_cb_user_param)) {
-               LOGE("failed to send video stream data.");
+       if (!player->video_decoded_cb(stream, player->video_decoded_cb_user_param)) {
+               LOGE("failed to send video decoded data.");
                goto ERROR;
        }
 
@@ -3212,7 +3362,7 @@ __mmplayer_gst_create_video_filters(mmplayer_t *player, MMDisplaySurfaceType sur
        /* create video360 filter */
        if (player->is_360_feature_enabled && player->is_content_spherical) {
                LOGD("create video360 element");
-               MMPLAYER_CREATE_ELEMENT(player->pipeline->videobin, MMPLAYER_V_360, "video360", "video-360", TRUE, player);
+               MMPLAYER_CREATE_ELEMENT(player->pipeline->videobin, MMPLAYER_V_360, "video360", "video-360", element_bucket, player);
                __mmplayer_gst_set_video360_property(player);
                goto EXIT;
        }
@@ -3225,7 +3375,7 @@ __mmplayer_gst_create_video_filters(mmplayer_t *player, MMDisplaySurfaceType sur
        /* in case of sw codec & overlay surface type, except 360 playback.
         * if libav video decoder is selected, videoconvert is required to render the shm wl-buffer which support RGB only via tizenwlsink. */
        LOGD("create video converter: %s", video_csc);
-       MMPLAYER_CREATE_ELEMENT(player->pipeline->videobin, MMPLAYER_V_CONV, video_csc, "video converter", TRUE, player);
+       MMPLAYER_CREATE_ELEMENT(player->pipeline->videobin, MMPLAYER_V_CONV, video_csc, "video converter", element_bucket, player);
 
 EXIT:
        *bucket = element_bucket;
@@ -3305,7 +3455,7 @@ __mmplayer_gst_set_videosink_property(mmplayer_t *player, MMDisplaySurfaceType s
                g_object_set(player->pipeline->videobin[MMPLAYER_V_SINK].gst, "enable-last-sample", FALSE, NULL);
        }
 
-       if (player->set_mode.media_packet_video_stream) {
+       if (player->set_mode.video_export) {
                int enable = 0;
                mm_attrs_get_int_by_name(player->attrs, "enable_video_decoded_cb", &enable);
                if (enable || (surface_type == MM_DISPLAY_SURFACE_REMOTE) || (surface_type == MM_DISPLAY_SURFACE_NULL))
@@ -3379,7 +3529,7 @@ __mmplayer_gst_create_video_sink_bin(mmplayer_t *player, GstCaps *caps, MMDispla
                goto ERROR;
 
        videosink_factory_name = __mmplayer_get_videosink_factory_name(player, surface_type);
-       MMPLAYER_CREATE_ELEMENT(videobin, MMPLAYER_V_SINK, videosink_factory_name, "videosink", TRUE, player);
+       MMPLAYER_CREATE_ELEMENT(videobin, MMPLAYER_V_SINK, videosink_factory_name, "videosink", element_bucket, player);
 
        /* additional setting for sink plug-in */
        if (__mmplayer_gst_set_videosink_property(player, surface_type) != MM_ERROR_NONE) {
@@ -3453,13 +3603,13 @@ __mmplayer_gst_create_plain_text_elements(mmplayer_t *player)
        GList *element_bucket = NULL;
        mmplayer_gst_element_t *textbin = player->pipeline->textbin;
 
-       MMPLAYER_CREATE_ELEMENT(textbin, MMPLAYER_T_QUEUE, "queue", "text_queue", TRUE, player);
-       MMPLAYER_CREATE_ELEMENT(textbin, MMPLAYER_T_IDENTITY, "identity", "text_identity", TRUE, player);
+       MMPLAYER_CREATE_ELEMENT(textbin, MMPLAYER_T_QUEUE, "queue", "text_queue", element_bucket, player);
+       MMPLAYER_CREATE_ELEMENT(textbin, MMPLAYER_T_IDENTITY, "identity", "text_identity", element_bucket, player);
        g_object_set(G_OBJECT(textbin[MMPLAYER_T_IDENTITY].gst),
                                                        "signal-handoffs", FALSE,
                                                        NULL);
 
-       MMPLAYER_CREATE_ELEMENT(textbin, MMPLAYER_T_FAKE_SINK, "fakesink", "text_fakesink", TRUE, player);
+       MMPLAYER_CREATE_ELEMENT(textbin, MMPLAYER_T_FAKE_SINK, "fakesink", "text_fakesink", element_bucket, player);
        __mmplayer_add_signal_connection(player,
                                                        G_OBJECT(textbin[MMPLAYER_T_FAKE_SINK].gst),
                                                        MM_PLAYER_SIGNAL_TYPE_TEXTBIN,
@@ -4283,7 +4433,7 @@ __mmplayer_can_do_interrupt(mmplayer_t *player)
                goto FAILED;
        }
 
-       if (player->audio_stream_render_cb) {
+       if (player->audio_decoded_cb) {
                LOGW("not support in pcm extraction mode");
                goto FAILED;
        }
@@ -4967,139 +5117,132 @@ _mmplayer_get_state(MMHandleType hplayer, int *state)
        return MM_ERROR_NONE;
 }
 
-
-int
-_mmplayer_set_volume(MMHandleType hplayer, mmplayer_volume_type_t volume)
+static int
+__mmplayer_gst_set_volume_property(mmplayer_t *player, const char *prop_name)
 {
-       mmplayer_t *player = (mmplayer_t *)hplayer;
        GstElement *vol_element = NULL;
-       int i = 0;
+       enum audio_element_id volume_elem_id = MMPLAYER_A_VOL;
 
        MMPLAYER_FENTER();
-
        MMPLAYER_RETURN_VAL_IF_FAIL(player, MM_ERROR_PLAYER_NOT_INITIALIZED);
-
-       LOGD("volume [L]=%f:[R]=%f",
-               volume.level[MM_VOLUME_CHANNEL_LEFT], volume.level[MM_VOLUME_CHANNEL_RIGHT]);
-
-       /* invalid factor range or not */
-       for (i = 0; i < MM_VOLUME_CHANNEL_NUM; i++) {
-               if (volume.level[i] < MM_VOLUME_FACTOR_MIN || volume.level[i] > MM_VOLUME_FACTOR_MAX) {
-                       LOGE("Invalid factor!(valid factor:0~1.0)");
-                       return MM_ERROR_INVALID_ARGUMENT;
-               }
-       }
-
-       /* not support to set other value into each channel */
-       if ((volume.level[MM_VOLUME_CHANNEL_LEFT] != volume.level[MM_VOLUME_CHANNEL_RIGHT]))
-               return MM_ERROR_INVALID_ARGUMENT;
-
-       /* Save volume to handle. Currently the first array element will be saved. */
-       player->sound.volume = volume.level[MM_VOLUME_CHANNEL_LEFT];
+       MMPLAYER_RETURN_VAL_IF_FAIL(prop_name, MM_ERROR_INVALID_ARGUMENT);
 
        /* check pipeline handle */
        if (!player->pipeline || !player->pipeline->audiobin) {
-               LOGD("audiobin is not created yet");
-               LOGD("but, current stored volume will be set when it's created.");
+               LOGD("'%s' will be applied when audiobin is created", prop_name);
 
-               /* NOTE : stored volume will be used in create_audiobin
+               /* NOTE : stored value will be used in create_audiobin
                 * returning MM_ERROR_NONE here makes application to able to
-                * set volume at anytime.
+                * set audio volume or mute at anytime.
                 */
                return MM_ERROR_NONE;
        }
 
-       /* setting volume to volume element */
-       vol_element = player->pipeline->audiobin[MMPLAYER_A_VOL].gst;
+       if (player->build_audio_offload) {
+               LOGD("offload pipeline");
+               volume_elem_id = MMPLAYER_A_SINK;
+       }
 
-       if (vol_element) {
-               LOGD("volume is set [%f]", player->sound.volume);
-               g_object_set(vol_element, "volume", player->sound.volume, NULL);
+       vol_element = player->pipeline->audiobin[volume_elem_id].gst;
+       if (!vol_element) {
+               LOGE("failed to get vol element %d", volume_elem_id);
+               return MM_ERROR_PLAYER_INTERNAL;
        }
 
-       MMPLAYER_FLEAVE();
+       LOGD("set '%s' property to element[%s]", prop_name, GST_ELEMENT_NAME(vol_element));
+
+       if (!g_object_class_find_property(G_OBJECT_GET_CLASS(vol_element), prop_name)) {
+               LOGE("there is no '%s' property", prop_name);
+               return MM_ERROR_PLAYER_INTERNAL;
+       }
+
+       if (!strcmp(prop_name, "volume")) {
+               g_object_set(vol_element, "volume", player->sound.volume, NULL);
+       } else if (!strcmp(prop_name, "mute")) {
+               g_object_set(vol_element, "mute", player->sound.mute, NULL);
+       } else {
+               LOGE("invalid property %s", prop_name);
+               return MM_ERROR_PLAYER_INTERNAL;
+       }
 
        return MM_ERROR_NONE;
 }
 
 int
-_mmplayer_get_volume(MMHandleType hplayer, mmplayer_volume_type_t *volume)
+_mmplayer_set_volume(MMHandleType hplayer, float volume)
 {
+       int ret = MM_ERROR_NONE;
        mmplayer_t *player = (mmplayer_t *)hplayer;
-       int i = 0;
 
        MMPLAYER_FENTER();
-
        MMPLAYER_RETURN_VAL_IF_FAIL(player, MM_ERROR_PLAYER_NOT_INITIALIZED);
-       MMPLAYER_RETURN_VAL_IF_FAIL(volume, MM_ERROR_INVALID_ARGUMENT);
 
-       /* returning stored volume */
-       for (i = 0; i < MM_VOLUME_CHANNEL_NUM; i++)
-               volume->level[i] = player->sound.volume;
+       LOGD("volume = %f", volume);
 
-       MMPLAYER_FLEAVE();
+       /* invalid factor range or not */
+       if (volume < MM_VOLUME_FACTOR_MIN || volume > MM_VOLUME_FACTOR_MAX) {
+               LOGE("Invalid volume value");
+               return MM_ERROR_INVALID_ARGUMENT;
+       }
 
-       return MM_ERROR_NONE;
+       player->sound.volume = volume;
+
+       ret = __mmplayer_gst_set_volume_property(player, "volume");
+
+       MMPLAYER_FLEAVE();
+       return ret;
 }
 
 int
-_mmplayer_set_mute(MMHandleType hplayer, int mute)
+_mmplayer_get_volume(MMHandleType hplayer, float *volume)
 {
        mmplayer_t *player = (mmplayer_t *)hplayer;
-       GstElement *vol_element = NULL;
 
        MMPLAYER_FENTER();
 
        MMPLAYER_RETURN_VAL_IF_FAIL(player, MM_ERROR_PLAYER_NOT_INITIALIZED);
+       MMPLAYER_RETURN_VAL_IF_FAIL(volume, MM_ERROR_INVALID_ARGUMENT);
 
-       /* mute value shoud 0 or 1 */
-       if (mute != 0 && mute != 1) {
-               LOGE("bad mute value");
+       *volume = player->sound.volume;
 
-               /* FIXIT : definitly, we need _BAD_PARAM error code */
-               return MM_ERROR_INVALID_ARGUMENT;
-       }
+       LOGD("current vol = %f", *volume);
 
-       player->sound.mute = mute;
+       MMPLAYER_FLEAVE();
+       return MM_ERROR_NONE;
+}
 
-       /* just hold mute value if pipeline is not ready */
-       if (!player->pipeline || !player->pipeline->audiobin) {
-               LOGD("pipeline is not ready. holding mute value");
-               return MM_ERROR_NONE;
-       }
+int
+_mmplayer_set_mute(MMHandleType hplayer, bool mute)
+{
+       int ret = MM_ERROR_NONE;
+       mmplayer_t *player = (mmplayer_t *)hplayer;
 
-       vol_element = player->pipeline->audiobin[MMPLAYER_A_VOL].gst;
+       MMPLAYER_FENTER();
+       MMPLAYER_RETURN_VAL_IF_FAIL(player, MM_ERROR_PLAYER_NOT_INITIALIZED);
 
-       /* NOTE : volume will only created when the bt is enabled */
-       if (vol_element) {
-               LOGD("mute : %d", mute);
-               g_object_set(vol_element, "mute", mute, NULL);
-       } else
-               LOGD("volume elemnet is not created. using volume in audiosink");
+       LOGD("mute = %d", mute);
 
-       MMPLAYER_FLEAVE();
+       player->sound.mute = mute;
 
-       return MM_ERROR_NONE;
+       ret = __mmplayer_gst_set_volume_property(player, "mute");
+
+       MMPLAYER_FLEAVE();
+       return ret;
 }
 
 int
-_mmplayer_get_mute(MMHandleType hplayer, int *pmute)
+_mmplayer_get_mute(MMHandleType hplayer, bool *mute)
 {
        mmplayer_t *player = (mmplayer_t *)hplayer;
 
        MMPLAYER_FENTER();
 
        MMPLAYER_RETURN_VAL_IF_FAIL(player, MM_ERROR_PLAYER_NOT_INITIALIZED);
-       MMPLAYER_RETURN_VAL_IF_FAIL(pmute, MM_ERROR_INVALID_ARGUMENT);
+       MMPLAYER_RETURN_VAL_IF_FAIL(mute, MM_ERROR_INVALID_ARGUMENT);
 
-       /* just hold mute value if pipeline is not ready */
-       if (!player->pipeline || !player->pipeline->audiobin) {
-               LOGD("pipeline is not ready. returning stored value");
-               *pmute = player->sound.mute;
-               return MM_ERROR_NONE;
-       }
+       *mute = player->sound.mute;
 
-       *pmute = player->sound.mute;
+       LOGD("current mute = %d", *mute);
 
        MMPLAYER_FLEAVE();
 
@@ -5143,7 +5286,7 @@ _mmplayer_set_audiostream_changed_cb(MMHandleType hplayer, mm_player_stream_chan
 }
 
 int
-_mmplayer_set_audiostream_cb(MMHandleType hplayer, bool sync, mm_player_audio_stream_callback callback, void *user_param)
+_mmplayer_set_audio_decoded_cb(MMHandleType hplayer, mmplayer_audio_extract_opt_e opt, mm_player_audio_decoded_callback callback, void *user_param)
 {
        mmplayer_t *player = (mmplayer_t *)hplayer;
 
@@ -5151,10 +5294,10 @@ _mmplayer_set_audiostream_cb(MMHandleType hplayer, bool sync, mm_player_audio_st
 
        MMPLAYER_RETURN_VAL_IF_FAIL(player, MM_ERROR_PLAYER_NOT_INITIALIZED);
 
-       player->audio_stream_render_cb = callback;
-       player->audio_stream_cb_user_param = user_param;
-       player->audio_stream_sink_sync = sync;
-       LOGD("handle: %p, cb: %p, sync: %d", player, player->audio_stream_render_cb, player->audio_stream_sink_sync);
+       player->audio_decoded_cb = callback;
+       player->audio_decoded_cb_user_param = user_param;
+       player->audio_extract_opt = opt;
+       LOGD("handle: %p, cb: %p, opt: 0x%X", player, player->audio_decoded_cb, player->audio_extract_opt);
 
        MMPLAYER_FLEAVE();
 
@@ -5162,7 +5305,7 @@ _mmplayer_set_audiostream_cb(MMHandleType hplayer, bool sync, mm_player_audio_st
 }
 
 int
-_mmplayer_set_videostream_cb(MMHandleType hplayer, mm_player_video_stream_callback callback, void *user_param)
+_mmplayer_set_video_decoded_cb(MMHandleType hplayer, mm_player_video_decoded_callback callback, void *user_param)
 {
        mmplayer_t *player = (mmplayer_t *)hplayer;
 
@@ -5173,11 +5316,11 @@ _mmplayer_set_videostream_cb(MMHandleType hplayer, mm_player_video_stream_callba
        if (callback && !player->bufmgr)
                player->bufmgr = tbm_bufmgr_init(-1);
 
-       player->set_mode.media_packet_video_stream = (callback) ? true : false;
-       player->video_stream_cb = callback;
-       player->video_stream_cb_user_param = user_param;
+       player->set_mode.video_export = (callback) ? true : false;
+       player->video_decoded_cb = callback;
+       player->video_decoded_cb_user_param = user_param;
 
-       LOGD("Stream cb Handle value is %p : %p, enable:%d", player, player->video_stream_cb, player->set_mode.media_packet_video_stream);
+       LOGD("Stream cb Handle value is %p : %p, enable:%d", player, player->video_decoded_cb, player->set_mode.video_export);
 
        MMPLAYER_FLEAVE();
 
@@ -5506,7 +5649,7 @@ _mmplayer_set_playspeed(MMHandleType hplayer, float rate, bool streaming)
        mmplayer_t *player = (mmplayer_t *)hplayer;
        gint64 pos_nsec = 0;
        int ret = MM_ERROR_NONE;
-       int mute = FALSE;
+       bool mute = false;
        signed long long start = 0, stop = 0;
        mmplayer_state_e current_state = MM_PLAYER_STATE_NONE;
        MMPLAYER_FENTER();
@@ -5517,7 +5660,7 @@ _mmplayer_set_playspeed(MMHandleType hplayer, float rate, bool streaming)
        /* The sound of video is not supported under 0.0 and over 2.0. */
        if (rate >= TRICK_PLAY_MUTE_THRESHOLD_MAX || rate < TRICK_PLAY_MUTE_THRESHOLD_MIN) {
                if (player->can_support_codec & FOUND_PLUGIN_VIDEO)
-                       mute = TRUE;
+                       mute = true;
        }
        _mmplayer_set_mute(hplayer, mute);
 
@@ -7208,7 +7351,7 @@ __mmplayer_gst_decode_autoplug_select(GstElement *bin,  GstPad *pad,
 
                /* don't make video because of not required */
                if ((stype == MM_DISPLAY_SURFACE_NULL) &&
-                       (!player->set_mode.media_packet_video_stream)) {
+                       (!player->set_mode.video_export)) {
                        LOGD("no need video decoding, expose pad");
                        result = GST_AUTOPLUG_SELECT_EXPOSE;
                        goto DONE;
@@ -7434,13 +7577,13 @@ __mmplayer_release_misc(mmplayer_t *player)
 
        MMPLAYER_RETURN_IF_FAIL(player);
 
-       player->video_stream_cb = NULL;
-       player->video_stream_cb_user_param = NULL;
+       player->video_decoded_cb = NULL;
+       player->video_decoded_cb_user_param = NULL;
        player->video_stream_prerolled = false;
 
-       player->audio_stream_render_cb = NULL;
-       player->audio_stream_cb_user_param = NULL;
-       player->audio_stream_sink_sync = false;
+       player->audio_decoded_cb = NULL;
+       player->audio_decoded_cb_user_param = NULL;
+       player->audio_extract_opt = MM_PLAYER_AUDIO_EXTRACT_DEFAULT;
 
        player->video_stream_changed_cb = NULL;
        player->video_stream_changed_cb_user_param = NULL;
@@ -7471,13 +7614,14 @@ __mmplayer_release_misc(mmplayer_t *player)
        player->play_subtitle = FALSE;
        player->adjust_subtitle_pos = 0;
        player->has_closed_caption = FALSE;
-       player->set_mode.media_packet_video_stream = false;
+       player->set_mode.video_export = false;
        player->profile.uri_type = MM_PLAYER_URI_TYPE_NONE;
        memset(&player->set_mode, 0, sizeof(mmplayer_setting_mode_t));
        /* recover mode */
        player->set_mode.rich_audio = cur_mode;
 
-       if (mm_sound_remove_device_connected_callback(player->audio_device_cb_id) != MM_ERROR_NONE)
+       if (player->audio_device_cb_id > 0 &&
+               mm_sound_remove_device_connected_callback(player->audio_device_cb_id) != MM_ERROR_NONE)
                LOGW("failed to remove audio device_connected_callback");
        player->audio_device_cb_id = 0;
 
@@ -8341,6 +8485,8 @@ __mmplayer_dump_buffer_probe_cb(GstPad *pad,  GstPadProbeInfo *info, gpointer u_
 
        fwrite(probe_info.data, 1, probe_info.size , dump_data);
 
+       gst_buffer_unmap(buffer, &probe_info);
+
        return GST_PAD_PROBE_OK;
 }
 
@@ -8849,17 +8995,25 @@ __mmplayer_update_audio_attrs(mmplayer_t *player, MMHandleType attrs)
        GstPad *pad = NULL;
        gint samplerate = 0, channels = 0;
        GstStructure *p = NULL;
+       GstElement *aconv = NULL;
 
        LOGD("try to update audio attrs");
 
        MMPLAYER_RETURN_VAL_IF_FAIL(player->pipeline->audiobin, FALSE);
-       MMPLAYER_RETURN_VAL_IF_FAIL(player->pipeline->audiobin[MMPLAYER_A_SINK].gst, FALSE);
 
-       pad = gst_element_get_static_pad(
-                       player->pipeline->audiobin[MMPLAYER_A_CONV].gst, "sink");
+       if (player->pipeline->audiobin[MMPLAYER_A_CONV].gst) {
+               aconv = player->pipeline->audiobin[MMPLAYER_A_CONV].gst;
+       } else if (player->pipeline->audiobin[MMPLAYER_A_EXTRACT_CONV].gst) {
+               aconv = player->pipeline->audiobin[MMPLAYER_A_EXTRACT_CONV].gst;
+       } else {
+               LOGE("there is no audio converter");
+               return FALSE;
+       }
+
+       pad = gst_element_get_static_pad(aconv, "sink");
 
        if (!pad) {
-               LOGW("failed to get pad from audiosink");
+               LOGW("failed to get pad from audio converter");
                return FALSE;
        }
 
@@ -9167,6 +9321,7 @@ static void
 __mmplayer_zerocopy_set_stride_elevation_bo(mmplayer_video_decoded_data_info_t *stream, GstMemory *mem)
 {
        unsigned int pitch = 0;
+       unsigned int size = 0;
        int index = 0;
        tbm_surface_h surface = gst_tizen_memory_get_surface(mem);
        tbm_bo bo = NULL;
@@ -9180,9 +9335,12 @@ __mmplayer_zerocopy_set_stride_elevation_bo(mmplayer_video_decoded_data_info_t *
        }
 
        for (index = 0; index < stream->plane_num; index++) {
-               tbm_surface_internal_get_plane_data(surface, index, NULL, NULL, &pitch);
+               tbm_surface_internal_get_plane_data(surface, index, &size, NULL, &pitch);
                stream->stride[index] = pitch;
-               stream->elevation[index] = stream->height;
+               if (pitch)
+                       stream->elevation[index] = size / pitch;
+               else
+                       stream->elevation[index] = stream->height;
        }
 }