+ /* 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,
+ MM_PLAYER_PCM_EXT_FORMAT, &dst_format, &dst_len,
+ MM_PLAYER_PCM_EXT_SAMPLERATE, &dst_samplerate,
+ MM_PLAYER_PCM_EXT_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 || _mmplayer_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;
+ }
+
+ /* 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);
+
+ caps_str = gst_caps_to_string(caps);
+ LOGD("new caps : %s", caps_str);
+
+ 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);
+ _mmplayer_add_signal_connection(player, G_OBJECT(audiobin[MMPLAYER_A_EXTRACT_DEINTERLEAVE].gst),
+ MM_PLAYER_SIGNAL_TYPE_AUTOPLUG, "no-more-pads", G_CALLBACK(__mmplayer_gst_audio_deinterleave_no_more_pads), (gpointer)player);
+ player->no_more_pad = FALSE;
+ } 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;
+ MMPLAYER_FLEAVE();
+ return MM_ERROR_PLAYER_INTERNAL;
+}
+
+static int
+__mmplayer_gst_create_audio_sink_bin(mmplayer_t *player)
+{
+ mmplayer_gst_element_t *first_element = NULL;
+ mmplayer_gst_element_t *audiobin = NULL;
+ GstPad *pad = NULL;
+ GstPad *ghostpad = NULL;
+ GList *element_bucket = NULL;
+ int i = 0;
+
+ MMPLAYER_FENTER();
+ MMPLAYER_RETURN_VAL_IF_FAIL(player && player->pipeline, MM_ERROR_PLAYER_NOT_INITIALIZED);
+
+ /* alloc handles */
+ audiobin = (mmplayer_gst_element_t *)g_try_malloc0(sizeof(mmplayer_gst_element_t) * MMPLAYER_A_NUM);
+ if (!audiobin) {
+ LOGE("failed to allocate memory for audiobin");
+ return MM_ERROR_PLAYER_NO_FREE_SPACE;
+ }
+
+ /* create bin */
+ audiobin[MMPLAYER_A_BIN].id = MMPLAYER_A_BIN;
+ audiobin[MMPLAYER_A_BIN].gst = gst_bin_new("audiobin");
+ if (!audiobin[MMPLAYER_A_BIN].gst) {
+ LOGE("failed to create audiobin");
+ goto ERROR;
+ }
+
+ /* take it */
+ player->pipeline->audiobin = audiobin;
+
+ /* create audio filters and audiosink */
+ if (__mmplayer_gst_make_audio_bin_element(player, &element_bucket) != MM_ERROR_NONE)
+ goto ERROR;
+
+ /* adding created elements to bin */
+ LOGD("adding created elements to bin");
+ if (!_mmplayer_gst_element_add_bucket_to_bin(GST_BIN(audiobin[MMPLAYER_A_BIN].gst), element_bucket))
+ goto ERROR;
+
+ /* linking elements in the bucket by added order. */
+ LOGD("Linking elements in the bucket by added order.");
+ if (_mmplayer_gst_element_link_bucket(element_bucket) == -1)
+ goto ERROR;
+
+ /* get first element's sinkpad for creating ghostpad */
+ first_element = (mmplayer_gst_element_t *)element_bucket->data;
+ if (!first_element) {
+ LOGE("failed to get first elem");
+ goto ERROR;
+ }
+
+ pad = gst_element_get_static_pad(GST_ELEMENT(first_element->gst), "sink");
+ if (!pad) {
+ LOGE("failed to get pad from first element of audiobin");
+ goto ERROR;
+ }
+
+ ghostpad = gst_ghost_pad_new("sink", pad);
+ if (!ghostpad) {
+ LOGE("failed to create ghostpad");
+ goto ERROR;
+ }
+
+ if (!gst_element_add_pad(audiobin[MMPLAYER_A_BIN].gst, ghostpad)) {
+ LOGE("failed to add ghostpad to audiobin");
+ goto ERROR;
+ }
+
+ gst_object_unref(pad);
+
+ g_list_free(element_bucket);
+ MMPLAYER_FLEAVE();
+
+ return MM_ERROR_NONE;
+
+ERROR:
+ LOGD("ERROR : releasing audiobin");