4 * Copyright (c) 2011 - 2013 Samsung Electronics Co., Ltd. All rights reserved.
6 * Contact: JongHyuk Choi <jhchoi.choi@samsung.com>, ByungWook Jang <bw.jang@samsung.com>,
7 * Maksym Ukhanov <m.ukhanov@samsung.com>, Hyunjun Ko <zzoon.ko@samsung.com>
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
24 #include <gst/video/videooverlay.h>
25 #include <Elementary.h>
26 #include <Ecore_Wl2.h>
27 #include <sys/syscall.h>
29 #include "mm_wfd_sink_util.h"
30 #include "mm_wfd_sink_priv.h"
31 #include "mm_wfd_sink_manager.h"
32 #include "mm_wfd_sink_dlog.h"
33 #include "mm_wfd_sink_enum.h"
34 #include "mm_wfd_sink_wayland.h"
35 #include "mm_wfd_sink_config.h"
37 #define PRINT_WFD_REF_COUNT(wfd_sink)\
39 wfd_sink_debug("PRINT WFD REF COUNT");\
40 __mm_wfd_sink_print_ref_count(wfd_sink);\
44 static int __mm_wfd_sink_init_gstreamer(mm_wfd_sink_t *wfd_sink);
45 static gboolean _mm_wfd_sink_msg_callback(GstBus *bus, GstMessage *msg, gpointer data);
48 static void __mm_wfd_sink_dump_pipeline_state(mm_wfd_sink_t *wfd_sink);
50 int _mm_wfd_sink_create(mm_wfd_sink_t **wfd_sink, const char *ini_path)
52 int result = MM_ERROR_NONE;
54 wfd_sink_debug_fenter();
56 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
57 if (ini_path == NULL) {
58 ini_path = MM_WFD_SINK_INI_DEFAULT_PATH;
59 wfd_sink_debug("The wfd ini file path is set as defalut[%s]", ini_path);
62 mm_wfd_sink_t *new_wfd_sink = NULL;
65 new_wfd_sink = g_malloc0(sizeof(mm_wfd_sink_t));
67 wfd_sink_error("failed to allocate memory for wi-fi display sink");
68 return MM_ERROR_WFD_NO_FREE_SPACE;
71 /* Initialize gstreamer related */
72 new_wfd_sink->attrs = 0;
74 new_wfd_sink->pipeline = NULL;
75 new_wfd_sink->audio_decodebin_is_linked = FALSE;
76 new_wfd_sink->video_decodebin_is_linked = FALSE;
78 /* Initialize timestamp compensation related */
79 new_wfd_sink->need_to_reset_basetime = FALSE;
80 new_wfd_sink->clock = NULL;
81 new_wfd_sink->video_buffer_count = 0LL;
82 new_wfd_sink->video_average_gap = 0LL;
83 new_wfd_sink->video_accumulated_gap = 0LL;
84 new_wfd_sink->audio_buffer_count = 0LL;
85 new_wfd_sink->audio_average_gap = 0LL;
86 new_wfd_sink->audio_accumulated_gap = 0LL;
87 new_wfd_sink->last_buffer_timestamp = GST_CLOCK_TIME_NONE;
89 /* Initialize all states */
90 MMWFDSINK_CURRENT_STATE(new_wfd_sink) = MM_WFD_SINK_STATE_NONE;
91 MMWFDSINK_PREVIOUS_STATE(new_wfd_sink) = MM_WFD_SINK_STATE_NONE;
92 MMWFDSINK_PENDING_STATE(new_wfd_sink) = MM_WFD_SINK_STATE_NONE;
94 /* initialize audio/video information */
95 new_wfd_sink->stream_info.audio_stream_info.codec = MM_WFD_SINK_AUDIO_CODEC_NONE;
96 new_wfd_sink->stream_info.audio_stream_info.channels = 0;
97 new_wfd_sink->stream_info.audio_stream_info.sample_rate = 0;
98 new_wfd_sink->stream_info.audio_stream_info.bitwidth = 0;
99 new_wfd_sink->stream_info.video_stream_info.codec = MM_WFD_SINK_VIDEO_CODEC_NONE;
100 new_wfd_sink->stream_info.video_stream_info.width = 0;
101 new_wfd_sink->stream_info.video_stream_info.height = 0;
102 new_wfd_sink->stream_info.video_stream_info.frame_rate = 0;
104 /* Initialize command */
105 new_wfd_sink->cmd = MM_WFD_SINK_COMMAND_CREATE;
106 new_wfd_sink->waiting_cmd = FALSE;
108 /* Initialize manager related */
109 new_wfd_sink->manager_thread = NULL;
110 new_wfd_sink->manager_thread_cmd = NULL;
111 new_wfd_sink->manager_thread_exit = FALSE;
113 /* Initialize video resolution */
114 new_wfd_sink->supportive_resolution = MM_WFD_SINK_RESOLUTION_UNKNOWN;
116 /* Initialize coulped sink information */
117 new_wfd_sink->coupled_sink_address = NULL;
118 new_wfd_sink->coupled_sink_status = MM_WFD_COUPLED_SINK_STATUS_NOT_COUPLED;
120 /* In case of R2 sink, it would be TRUE */
121 new_wfd_sink->is_coupled_sink_supported = FALSE;
123 /* construct attributes */
124 new_wfd_sink->attrs = _mmwfd_construct_attribute((MMHandleType)new_wfd_sink);
125 if (!new_wfd_sink->attrs) {
126 MMWFDSINK_FREEIF(new_wfd_sink);
127 wfd_sink_error("failed to set attribute");
128 return MM_ERROR_WFD_INTERNAL;
131 /* load ini for initialize */
132 result = mm_wfd_sink_ini_load(&new_wfd_sink->ini, ini_path);
133 if (result != MM_ERROR_NONE) {
134 wfd_sink_error("failed to load ini file[%s]", ini_path);
135 goto fail_to_load_ini;
137 new_wfd_sink->need_to_reset_basetime = new_wfd_sink->ini.enable_reset_basetime;
139 /* initialize manager */
140 result = _mm_wfd_sink_init_manager(new_wfd_sink);
141 if (result < MM_ERROR_NONE) {
142 wfd_sink_error("failed to init manager : %d", result);
146 /* initialize gstreamer */
147 result = __mm_wfd_sink_init_gstreamer(new_wfd_sink);
148 if (result < MM_ERROR_NONE) {
149 wfd_sink_error("failed to init gstreamer : %d", result);
154 __mm_wfd_sink_set_state(new_wfd_sink, MM_WFD_SINK_STATE_NULL);
156 /* now take handle */
157 *wfd_sink = new_wfd_sink;
159 wfd_sink_debug_fleave();
165 mm_wfd_sink_ini_unload(&new_wfd_sink->ini);
167 _mmwfd_deconstruct_attribute(new_wfd_sink->attrs);
168 MMWFDSINK_FREEIF(new_wfd_sink);
175 int _mm_wfd_sink_prepare(mm_wfd_sink_t *wfd_sink)
177 int result = MM_ERROR_NONE;
179 wfd_sink_debug_fenter();
181 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
183 /* check current wi-fi display sink state */
184 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_PREPARE);
186 /* construct pipeline */
187 /* create main pipeline */
188 result = __mm_wfd_sink_create_pipeline(wfd_sink);
189 if (result < MM_ERROR_NONE) {
190 wfd_sink_error("failed to create pipeline : %d", result);
194 /* create video decodebin */
195 result = __mm_wfd_sink_create_video_decodebin(wfd_sink);
196 if (result < MM_ERROR_NONE) {
197 wfd_sink_error("failed to create video decodebin %d", result);
201 /* create video sinkbin */
202 result = __mm_wfd_sink_create_video_sinkbin(wfd_sink);
203 if (result < MM_ERROR_NONE) {
204 wfd_sink_error("failed to create video sinkbin %d", result);
208 /* create audio decodebin */
209 result = __mm_wfd_sink_create_audio_decodebin(wfd_sink);
210 if (result < MM_ERROR_NONE) {
211 wfd_sink_error("fail to create audio decodebin : %d", result);
215 /* create audio sinkbin */
216 result = __mm_wfd_sink_create_audio_sinkbin(wfd_sink);
217 if (result < MM_ERROR_NONE) {
218 wfd_sink_error("fail to create audio sinkbin : %d", result);
222 /* set pipeline READY state */
223 result = __mm_wfd_sink_set_pipeline_state(wfd_sink, GST_STATE_READY, TRUE);
224 if (result < MM_ERROR_NONE) {
225 wfd_sink_error("failed to set state : %d", result);
230 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_PREPARED);
232 wfd_sink_debug_fleave();
238 /* need to destroy pipeline already created */
239 __mm_wfd_sink_destroy_pipeline(wfd_sink);
243 int _mm_wfd_sink_connect(mm_wfd_sink_t *wfd_sink, const char *uri)
245 int result = MM_ERROR_NONE;
247 wfd_sink_debug_fenter();
249 wfd_sink_return_val_if_fail(uri && strlen(uri) > strlen("rtsp://"),
250 MM_ERROR_WFD_INVALID_ARGUMENT);
251 wfd_sink_return_val_if_fail(wfd_sink &&
252 wfd_sink->pipeline &&
253 wfd_sink->pipeline->mainbin &&
254 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst &&
255 wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst &&
256 wfd_sink->pipeline->mainbin[WFD_SINK_M_DEPAY].gst &&
257 wfd_sink->pipeline->mainbin[WFD_SINK_M_DEMUX].gst,
258 MM_ERROR_WFD_NOT_INITIALIZED);
260 /* check current wi-fi display sink state */
261 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_CONNECT);
263 wfd_sink_debug("try to connect to %s.....", GST_STR_NULL(uri));
265 /* set uri to wfdsrc */
266 g_object_set(G_OBJECT(wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst), "location", uri, NULL);
268 /* set pipeline PAUSED state */
269 result = __mm_wfd_sink_set_pipeline_state(wfd_sink, GST_STATE_PAUSED, TRUE);
270 if (result < MM_ERROR_NONE) {
271 wfd_sink_error("failed to set state : %d", result);
275 wfd_sink_debug_fleave();
280 int _mm_wfd_sink_start(mm_wfd_sink_t *wfd_sink)
282 int result = MM_ERROR_NONE;
284 wfd_sink_debug_fenter();
286 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
288 /* check current wi-fi display sink state */
289 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_START);
291 WFD_SINK_MANAGER_LOCK(wfd_sink) ;
292 wfd_sink_debug("check pipeline is ready to start");
293 WFD_SINK_MANAGER_UNLOCK(wfd_sink);
295 result = __mm_wfd_sink_set_pipeline_state(wfd_sink, GST_STATE_PLAYING, TRUE);
296 if (result < MM_ERROR_NONE) {
297 wfd_sink_error("failed to set state : %d", result);
301 wfd_sink_debug_fleave();
306 int _mm_wfd_sink_pause(mm_wfd_sink_t *wfd_sink)
308 int result = MM_ERROR_NONE;
310 wfd_sink_debug_fenter();
312 wfd_sink_return_val_if_fail(wfd_sink &&
313 wfd_sink->pipeline &&
314 wfd_sink->pipeline->mainbin &&
315 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst &&
316 wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst,
317 MM_ERROR_WFD_NOT_INITIALIZED);
319 /* check current wi-fi display sink state */
320 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_PAUSE);
322 g_signal_emit_by_name(wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst, "request-pause", NULL);
324 wfd_sink_debug_fleave();
329 int _mm_wfd_sink_resume(mm_wfd_sink_t *wfd_sink)
331 int result = MM_ERROR_NONE;
333 wfd_sink_debug_fenter();
335 wfd_sink_return_val_if_fail(wfd_sink &&
336 wfd_sink->pipeline &&
337 wfd_sink->pipeline->mainbin &&
338 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst &&
339 wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst,
340 MM_ERROR_WFD_NOT_INITIALIZED);
342 /* check current wi-fi display sink state */
343 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_RESUME);
345 g_signal_emit_by_name(wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst, "request-resume", NULL);
347 wfd_sink_debug_fleave();
352 int _mm_wfd_sink_disconnect(mm_wfd_sink_t *wfd_sink)
354 int result = MM_ERROR_NONE;
356 wfd_sink_debug_fenter();
358 wfd_sink_return_val_if_fail(wfd_sink &&
359 wfd_sink->pipeline &&
360 wfd_sink->pipeline->mainbin &&
361 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst &&
362 wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst,
363 MM_ERROR_WFD_NOT_INITIALIZED);
365 /* check current wi-fi display sink state */
366 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_DISCONNECT);
368 WFD_SINK_MANAGER_APPEND_CMD(wfd_sink, WFD_SINK_MANAGER_CMD_EXIT);
369 WFD_SINK_MANAGER_SIGNAL_CMD(wfd_sink);
371 g_signal_emit_by_name(wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst, "request-close", NULL);
373 wfd_sink_debug_fleave();
378 int _mm_wfd_sink_unprepare(mm_wfd_sink_t *wfd_sink)
380 int result = MM_ERROR_NONE;
382 wfd_sink_debug_fenter();
384 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
386 /* check current wi-fi display sink state */
387 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_UNPREPARE);
389 WFD_SINK_MANAGER_APPEND_CMD(wfd_sink, WFD_SINK_MANAGER_CMD_EXIT);
390 WFD_SINK_MANAGER_SIGNAL_CMD(wfd_sink);
392 /* release pipeline */
393 result = __mm_wfd_sink_destroy_pipeline(wfd_sink);
394 if (result != MM_ERROR_NONE) {
395 wfd_sink_error("failed to destory pipeline");
396 return MM_ERROR_WFD_INTERNAL;
398 wfd_sink_debug("success to destory pipeline");
402 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_NULL);
404 wfd_sink_debug_fleave();
409 int _mm_wfd_sink_destroy(mm_wfd_sink_t *wfd_sink)
411 int result = MM_ERROR_NONE;
413 wfd_sink_debug_fenter();
415 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
417 /* check current wi-fi display sink state */
418 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_DESTROY);
421 mm_wfd_sink_ini_unload(&wfd_sink->ini);
423 /* release attributes */
424 _mmwfd_deconstruct_attribute(wfd_sink->attrs);
426 /* release the others */
427 g_free(wfd_sink->coupled_sink_address);
429 /* release manager thread */
430 if (MM_ERROR_NONE != _mm_wfd_sink_release_manager(wfd_sink)) {
431 wfd_sink_error("failed to release manager");
432 return MM_ERROR_WFD_INTERNAL;
436 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_NONE);
438 wfd_sink_debug_fleave();
443 int _mm_wfd_set_message_callback(mm_wfd_sink_t *wfd_sink, MMWFDMessageCallback callback, void *user_data)
445 int result = MM_ERROR_NONE;
447 wfd_sink_debug_fenter();
449 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
451 wfd_sink->msg_cb = callback;
452 wfd_sink->msg_user_data = user_data;
454 wfd_sink_debug_fleave();
459 static int __mm_wfd_sink_init_gstreamer(mm_wfd_sink_t *wfd_sink)
461 int result = MM_ERROR_NONE;
464 static const int max_argc = 50;
468 wfd_sink_debug_fenter();
471 argc = calloc(1, sizeof(gint));
472 argv = calloc(max_argc, sizeof(gchar *));
473 if (!argc || !argv) {
474 wfd_sink_error("failed to allocate memory for wfdsink");
476 MMWFDSINK_FREEIF(argv);
477 MMWFDSINK_FREEIF(argc);
479 return MM_ERROR_WFD_NO_FREE_SPACE;
482 /* we would not do fork for scanning plugins */
483 argv[*argc] = g_strdup("--gst-disable-registry-fork");
486 /* check disable registry scan */
487 argv[*argc] = g_strdup("--gst-disable-registry-update");
490 /* check disable segtrap */
491 argv[*argc] = g_strdup("--gst-disable-segtrap");
495 for (i = 0; i < 5; i++) {
496 if (strlen(wfd_sink->ini.gst_param[i]) > 2) {
497 wfd_sink_debug("set %s", wfd_sink->ini.gst_param[i]);
498 argv[*argc] = g_strdup(wfd_sink->ini.gst_param[i]);
503 wfd_sink_debug("initializing gstreamer with following parameter");
504 wfd_sink_debug("argc : %d", *argc);
506 for (i = 0; i < *argc; i++)
507 wfd_sink_debug("argv[%d] : %s", i, argv[i]);
509 /* initializing gstreamer */
510 if (!gst_init_check(argc, &argv, &err)) {
511 wfd_sink_error("failed to initialize gstreamer: %s",
512 err ? err->message : "unknown error occurred");
516 result = MM_ERROR_WFD_INTERNAL;
520 for (i = 0; i < *argc; i++)
521 MMWFDSINK_FREEIF(argv[i]);
523 MMWFDSINK_FREEIF(argv);
524 MMWFDSINK_FREEIF(argc);
526 wfd_sink_debug_fleave();
531 static GstBusSyncReply
532 _mm_wfd_bus_sync_callback(GstBus *bus, GstMessage *message, gpointer data)
534 GstBusSyncReply ret = GST_BUS_PASS;
536 wfd_sink_return_val_if_fail(message &&
537 GST_IS_MESSAGE(message) &&
538 GST_MESSAGE_SRC(message),
541 wfd_sink_debug("get message %p, %s from %p, %s", message,
542 GST_MESSAGE_TYPE_NAME(message), GST_MESSAGE_SRC(message), GST_MESSAGE_SRC_NAME(message));
544 switch (GST_MESSAGE_TYPE(message)) {
545 case GST_MESSAGE_TAG:
547 case GST_MESSAGE_DURATION:
549 case GST_MESSAGE_STATE_CHANGED:
550 /* we only handle state change messages from pipeline */
551 if (GST_IS_PIPELINE(GST_MESSAGE_SRC(message)))
552 _mm_wfd_sink_msg_callback(bus, message, data);
556 case GST_MESSAGE_ASYNC_DONE:
557 if (GST_IS_PIPELINE(GST_MESSAGE_SRC(message)))
558 _mm_wfd_sink_msg_callback(bus, message, data);
566 if (ret == GST_BUS_DROP) {
567 gst_message_unref(message);
574 int __mm_wfd_sink_activate_audio_stream(mm_wfd_sink_t *wfd_sink)
576 GstElement *valve = NULL;
577 GstPad *sinkpad = NULL;
578 GstPad *srcpad = NULL;
579 int result = MM_ERROR_NONE;
581 wfd_sink_debug_fenter();
583 wfd_sink_return_val_if_fail(wfd_sink &&
584 wfd_sink->pipeline &&
585 wfd_sink->pipeline->mainbin &&
586 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst &&
587 wfd_sink->pipeline->mainbin[WFD_SINK_M_A_VALVE].gst,
588 MM_ERROR_WFD_NOT_INITIALIZED);
590 valve = wfd_sink->pipeline->mainbin[WFD_SINK_M_A_VALVE].gst;
591 srcpad = gst_element_get_static_pad(valve, "src");
593 wfd_sink_error("failed to get src pad from %s", GST_ELEMENT_NAME(valve));
597 if (gst_pad_is_linked(srcpad)) {
598 wfd_sink_debug("%s:%s is already linked",
599 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad));
603 result = __mm_wfd_sink_prepare_audio_pipeline(wfd_sink, &sinkpad);
604 if (MM_ERROR_NONE != result) {
605 wfd_sink_error("failed to prepare audio pipeline....");
609 wfd_sink_debug("try to link %s:%s and %s:%s",
610 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
611 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
612 if (GST_PAD_LINK_OK != gst_pad_link_full(srcpad, sinkpad, GST_PAD_LINK_CHECK_NOTHING)) {
613 wfd_sink_error("failed to link %s:%s and %s:%s",
614 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
615 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
619 if (sinkpad != NULL) {
620 gst_object_unref(GST_OBJECT(sinkpad));
625 if (srcpad != NULL) {
626 gst_object_unref(GST_OBJECT(srcpad));
629 /* drop all the audio buffers using valve */
630 g_object_set(G_OBJECT(valve), "drop", FALSE, NULL);
632 wfd_sink_debug_fleave();
634 return MM_ERROR_NONE;
637 if (srcpad != NULL) {
638 gst_object_unref(GST_OBJECT(srcpad));
642 if (sinkpad != NULL) {
643 gst_object_unref(GST_OBJECT(sinkpad));
647 wfd_sink_debug_fleave();
648 return MM_ERROR_WFD_INTERNAL;
651 int __mm_wfd_sink_activate_video_stream(mm_wfd_sink_t *wfd_sink)
653 GstElement *valve = NULL;
654 GstPad *sinkpad = NULL;
655 GstPad *srcpad = NULL;
656 int result = MM_ERROR_NONE;
658 wfd_sink_debug_fenter();
660 wfd_sink_return_val_if_fail(wfd_sink &&
661 wfd_sink->pipeline &&
662 wfd_sink->pipeline->mainbin &&
663 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst &&
664 wfd_sink->pipeline->mainbin[WFD_SINK_M_V_VALVE].gst,
665 MM_ERROR_WFD_NOT_INITIALIZED);
667 valve = wfd_sink->pipeline->mainbin[WFD_SINK_M_V_VALVE].gst;
668 srcpad = gst_element_get_static_pad(valve, "src");
670 wfd_sink_error("failed to get src pad from %s", GST_ELEMENT_NAME(valve));
674 if (gst_pad_is_linked(srcpad)) {
675 wfd_sink_debug("%s:%s is already linked",
676 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad));
680 result = __mm_wfd_sink_prepare_video_pipeline(wfd_sink, &sinkpad);
681 if (MM_ERROR_NONE != result) {
682 wfd_sink_error("failed to prepare video pipeline....");
686 wfd_sink_debug("try to link %s:%s and %s:%s",
687 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
688 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
689 if (GST_PAD_LINK_OK != gst_pad_link_full(srcpad, sinkpad, GST_PAD_LINK_CHECK_NOTHING)) {
690 wfd_sink_error("failed to link %s:%s and %s:%s",
691 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
692 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
695 if (sinkpad != NULL) {
696 gst_object_unref(GST_OBJECT(sinkpad));
701 if (srcpad != NULL) {
702 gst_object_unref(GST_OBJECT(srcpad));
705 /* drop all the video buffers using valve */
706 g_object_set(G_OBJECT(valve), "drop", FALSE, NULL);
708 wfd_sink_debug_fleave();
710 return MM_ERROR_NONE;
713 if (srcpad != NULL) {
714 gst_object_unref(GST_OBJECT(srcpad));
718 if (sinkpad != NULL) {
719 gst_object_unref(GST_OBJECT(sinkpad));
723 wfd_sink_debug_fleave();
724 return MM_ERROR_WFD_INTERNAL;
727 int __mm_wfd_sink_deactivate_audio_stream(mm_wfd_sink_t *wfd_sink, gboolean unprepare)
729 int ret = MM_ERROR_NONE;
731 wfd_sink_debug_fenter();
733 wfd_sink_return_val_if_fail(wfd_sink &&
734 wfd_sink->pipeline &&
735 wfd_sink->pipeline->mainbin &&
736 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst &&
737 wfd_sink->pipeline->mainbin[WFD_SINK_M_A_VALVE].gst,
738 MM_ERROR_WFD_NOT_INITIALIZED);
740 /* drop all the audio buffers using valve */
741 g_object_set(G_OBJECT(wfd_sink->pipeline->mainbin[WFD_SINK_M_A_VALVE].gst), "drop", TRUE, NULL);
744 /* unprepare audio pipeline */
745 ret = __mm_wfd_sink_unprepare_audio_pipeline(wfd_sink);
746 if (ret != MM_ERROR_NONE) {
747 wfd_sink_error("failed to unprepare audio pipeline...");
748 return MM_ERROR_WFD_INTERNAL;
752 wfd_sink_debug_fleave();
754 return MM_ERROR_NONE;
757 int __mm_wfd_sink_deactivate_video_stream(mm_wfd_sink_t *wfd_sink, gboolean unprepare)
759 int ret = MM_ERROR_NONE;
761 wfd_sink_debug_fenter();
763 wfd_sink_return_val_if_fail(wfd_sink &&
764 wfd_sink->pipeline &&
765 wfd_sink->pipeline->mainbin &&
766 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst &&
767 wfd_sink->pipeline->mainbin[WFD_SINK_M_V_VALVE].gst,
768 MM_ERROR_WFD_NOT_INITIALIZED);
770 /* drop all the video buffers using valve */
771 g_object_set(G_OBJECT(wfd_sink->pipeline->mainbin[WFD_SINK_M_V_VALVE].gst), "drop", TRUE, NULL);
774 /* unprepare video pipeline */
775 ret = __mm_wfd_sink_unprepare_video_pipeline(wfd_sink);
776 if (ret != MM_ERROR_NONE) {
777 wfd_sink_error("failed to unprepare video pipeline...");
778 return MM_ERROR_WFD_INTERNAL;
782 wfd_sink_debug_fleave();
784 return MM_ERROR_NONE;
789 _mm_wfd_sink_msg_callback(GstBus *bus, GstMessage *msg, gpointer data)
791 mm_wfd_sink_t *wfd_sink = (mm_wfd_sink_t *) data;
792 const GstStructure *message_structure = gst_message_get_structure(msg);
794 gchar *getname = NULL;
796 wfd_sink_return_val_if_fail(wfd_sink, FALSE);
797 wfd_sink_return_val_if_fail(msg && GST_IS_MESSAGE(msg), FALSE);
799 wfd_sink_debug("got %s(%d) from %s",
800 GST_STR_NULL(GST_MESSAGE_TYPE_NAME(msg)),
801 GST_MESSAGE_TYPE(msg),
802 GST_STR_NULL(GST_OBJECT_NAME(GST_MESSAGE_SRC(msg))));
804 switch (GST_MESSAGE_TYPE(msg)) {
805 case GST_MESSAGE_ERROR: {
806 GError *error = NULL;
810 gst_message_parse_error(msg, &error, &debug);
812 wfd_sink_error("error : %s", error->message);
813 wfd_sink_error("debug : %s", debug);
815 MMWFDSINK_FREEIF(debug);
820 case GST_MESSAGE_WARNING: {
822 GError *error = NULL;
824 gst_message_parse_warning(msg, &error, &debug);
826 wfd_sink_error("warning : %s", error->message);
827 wfd_sink_error("debug : %s", debug);
829 MMWFDSINK_FREEIF(debug);
834 case GST_MESSAGE_STATE_CHANGED: {
835 const GValue *voldstate, *vnewstate, *vpending;
836 GstState oldstate, newstate, pending;
837 const GstStructure *structure;
839 /* get state info from msg */
840 structure = gst_message_get_structure(msg);
841 if (structure == NULL)
844 voldstate = gst_structure_get_value(structure, "old-state");
845 vnewstate = gst_structure_get_value(structure, "new-state");
846 vpending = gst_structure_get_value(structure, "pending-state");
847 if (voldstate == NULL || vnewstate == NULL || vpending == NULL)
850 oldstate = (GstState)voldstate->data[0].v_int;
851 newstate = (GstState)vnewstate->data[0].v_int;
852 pending = (GstState)vpending->data[0].v_int;
854 wfd_sink_debug("state changed [%s] : %s--->%s final : %s",
855 GST_OBJECT_NAME(GST_MESSAGE_SRC(msg)),
856 gst_element_state_get_name((GstState)oldstate),
857 gst_element_state_get_name((GstState)newstate),
858 gst_element_state_get_name((GstState)pending));
860 /* we only handle messages from pipeline */
861 if (msg->src != (GstObject *)wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst)
864 if (oldstate == newstate) {
865 wfd_sink_debug("pipeline reports state transition to old state");
870 case GST_STATE_VOID_PENDING:
872 case GST_STATE_READY:
873 case GST_STATE_PAUSED:
874 case GST_STATE_PLAYING:
881 case GST_MESSAGE_CLOCK_LOST: {
882 GstClock *clock = NULL;
883 gst_message_parse_clock_lost(msg, &clock);
884 wfd_sink_debug("The current clock[%s] as selected by the pipeline became unusable.",
885 (clock ? GST_OBJECT_NAME(clock) : "NULL"));
889 case GST_MESSAGE_NEW_CLOCK: {
890 GstClock *clock = NULL;
891 gst_message_parse_new_clock(msg, &clock);
895 if (wfd_sink->clock) {
896 if (wfd_sink->clock != clock)
897 wfd_sink_debug("clock is changed! [%s] -->[%s]",
898 GST_STR_NULL(GST_OBJECT_NAME(wfd_sink->clock)),
899 GST_STR_NULL(GST_OBJECT_NAME(clock)));
901 wfd_sink_debug("same clock is selected again! [%s]",
902 GST_STR_NULL(GST_OBJECT_NAME(clock)));
904 wfd_sink_debug("new clock [%s] was selected in the pipeline",
905 (GST_STR_NULL(GST_OBJECT_NAME(clock))));
908 wfd_sink->clock = clock;
912 case GST_MESSAGE_APPLICATION: {
913 const gchar *message_structure_name;
915 message_structure_name = gst_structure_get_name(message_structure);
916 if (!message_structure_name)
919 wfd_sink_debug("message name : %s", GST_STR_NULL(message_structure_name));
923 case GST_MESSAGE_ELEMENT: {
924 const gchar *structure_name = NULL;
926 structure_name = gst_structure_get_name(message_structure);
927 if (structure_name) {
928 wfd_sink_debug("got element specific message[%s]", GST_STR_NULL(structure_name));
929 if (g_strrstr(structure_name, "GstUDPSrcTimeout")) {
930 wfd_sink_error("Got %s, post error message", GST_STR_NULL(structure_name));
931 MMWFDSINK_POST_MESSAGE(wfd_sink,
932 MM_ERROR_WFD_INTERNAL,
933 MMWFDSINK_CURRENT_STATE(wfd_sink));
934 } else if (g_strrstr(structure_name, "GstWFDSessionTimeout")) {
935 wfd_sink_error("Got %s, post error message", GST_STR_NULL(structure_name));
936 MMWFDSINK_POST_MESSAGE(wfd_sink,
937 MM_ERROR_WFD_INTERNAL,
938 MM_WFD_SINK_STATE_DISCONNECTED);
944 case GST_MESSAGE_PROGRESS: {
945 GstProgressType type = GST_PROGRESS_TYPE_ERROR;
946 gchar *category = NULL, *text = NULL;
948 gst_message_parse_progress(msg, &type, &category, &text);
949 wfd_sink_debug("%s : %s ", GST_STR_NULL(category), GST_STR_NULL(text));
952 case GST_PROGRESS_TYPE_START:
954 case GST_PROGRESS_TYPE_COMPLETE:
955 if (category && !strcmp(category, "open")) {
956 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_CONNECTED);
957 } else if (category && !strcmp(category, "play")) {
958 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_PLAYING);
959 /*_mm_wfd_sink_correct_pipeline_latency (wfd_sink); */
960 } else if (category && !strcmp(category, "pause")) {
961 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_PAUSED);
962 } else if (category && !strcmp(category, "close")) {
963 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_DISCONNECTED);
964 } else if (category && !strcmp(category, "coupling")) {
965 wfd_sink->coupled_sink_status = MM_WFD_COUPLED_SINK_STATUS_COUPLED;
966 wfd_sink->coupled_sink_address = g_strdup(text);
967 wfd_sink_debug("coupling info [%d : %s]", wfd_sink->coupled_sink_status, wfd_sink->coupled_sink_address);
968 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_DISCONNECTED);
971 case GST_PROGRESS_TYPE_CANCELED:
973 case GST_PROGRESS_TYPE_ERROR:
974 if (category && !strcmp(category, "open")) {
975 wfd_sink_error("got error : %s", GST_STR_NULL(text));
976 /*_mm_wfd_sink_disconnect (wfd_sink); */
977 MMWFDSINK_POST_MESSAGE(wfd_sink,
978 MM_ERROR_WFD_INTERNAL,
979 MMWFDSINK_CURRENT_STATE(wfd_sink));
980 } else if (category && !strcmp(category, "play")) {
981 wfd_sink_error("got error : %s", GST_STR_NULL(text));
982 /*_mm_wfd_sink_disconnect (wfd_sink); */
983 MMWFDSINK_POST_MESSAGE(wfd_sink,
984 MM_ERROR_WFD_INTERNAL,
985 MMWFDSINK_CURRENT_STATE(wfd_sink));
986 } else if (category && !strcmp(category, "pause")) {
987 wfd_sink_error("got error : %s", GST_STR_NULL(text));
988 /*_mm_wfd_sink_disconnect (wfd_sink); */
989 MMWFDSINK_POST_MESSAGE(wfd_sink,
990 MM_ERROR_WFD_INTERNAL,
991 MMWFDSINK_CURRENT_STATE(wfd_sink));
992 } else if (category && !strcmp(category, "close")) {
993 wfd_sink_error("got error : %s", GST_STR_NULL(text));
994 /*_mm_wfd_sink_disconnect (wfd_sink); */
995 MMWFDSINK_POST_MESSAGE(wfd_sink,
996 MM_ERROR_WFD_INTERNAL,
997 MMWFDSINK_CURRENT_STATE(wfd_sink));
998 } else if (category && !strcmp(category, "coupling")) {
999 wfd_sink_error("got error : %s", GST_STR_NULL(text));
1000 /*_mm_wfd_sink_disconnect (wfd_sink); */
1001 MMWFDSINK_POST_MESSAGE(wfd_sink,
1002 MM_ERROR_WFD_INTERNAL,
1003 MMWFDSINK_CURRENT_STATE(wfd_sink));
1005 wfd_sink_error("got error : %s", GST_STR_NULL(text));
1009 wfd_sink_error("progress message has no type");
1013 MMWFDSINK_FREEIF(category);
1014 MMWFDSINK_FREEIF(text);
1018 case GST_MESSAGE_ASYNC_START:
1019 getname = gst_element_get_name(GST_MESSAGE_SRC(msg));
1020 wfd_sink_debug("GST_MESSAGE_ASYNC_START : %s", getname);
1021 MMWFDSINK_FREEIF(getname);
1023 case GST_MESSAGE_ASYNC_DONE:
1024 getname = gst_element_get_name(GST_MESSAGE_SRC(msg));
1025 wfd_sink_debug("GST_MESSAGE_ASYNC_DONE : %s", getname);
1026 MMWFDSINK_FREEIF(getname);
1028 case GST_MESSAGE_UNKNOWN:
1029 case GST_MESSAGE_INFO:
1030 case GST_MESSAGE_TAG:
1031 case GST_MESSAGE_BUFFERING:
1032 case GST_MESSAGE_EOS:
1033 case GST_MESSAGE_STATE_DIRTY:
1034 case GST_MESSAGE_STEP_DONE:
1035 case GST_MESSAGE_CLOCK_PROVIDE:
1036 case GST_MESSAGE_STRUCTURE_CHANGE:
1037 case GST_MESSAGE_STREAM_STATUS:
1038 case GST_MESSAGE_SEGMENT_START:
1039 case GST_MESSAGE_SEGMENT_DONE:
1040 case GST_MESSAGE_DURATION:
1041 case GST_MESSAGE_LATENCY:
1042 case GST_MESSAGE_REQUEST_STATE:
1043 case GST_MESSAGE_STEP_START:
1044 case GST_MESSAGE_QOS:
1045 case GST_MESSAGE_ANY:
1048 wfd_sink_debug("unhandled message");
1056 __mm_wfd_sink_gst_element_add_bucket_to_bin(GstBin *bin, GList *element_bucket, gboolean need_prepare)
1058 GList *bucket = element_bucket;
1059 MMWFDSinkGstElement *element = NULL;
1060 int successful_add_count = 0;
1062 wfd_sink_debug_fenter();
1064 wfd_sink_return_val_if_fail(element_bucket, 0);
1065 wfd_sink_return_val_if_fail(bin, 0);
1067 for (; bucket; bucket = bucket->next) {
1068 element = (MMWFDSinkGstElement *)bucket->data;
1070 if (element && element->gst) {
1072 gst_element_set_state(GST_ELEMENT(element->gst), GST_STATE_READY);
1074 if (!gst_bin_add(GST_BIN(bin), GST_ELEMENT(element->gst))) {
1075 wfd_sink_error("failed to add element [%s] to bin [%s]",
1076 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT(element->gst))),
1077 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT_CAST(bin))));
1081 wfd_sink_debug("add element [%s] to bin [%s]",
1082 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT(element->gst))),
1083 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT_CAST(bin))));
1085 successful_add_count++;
1089 wfd_sink_debug_fleave();
1091 return successful_add_count;
1095 __mm_wfd_sink_gst_element_link_bucket(GList *element_bucket)
1097 GList *bucket = element_bucket;
1098 MMWFDSinkGstElement *element = NULL;
1099 MMWFDSinkGstElement *prv_element = NULL;
1100 gint successful_link_count = 0;
1102 wfd_sink_debug_fenter();
1104 wfd_sink_return_val_if_fail(element_bucket, -1);
1106 prv_element = (MMWFDSinkGstElement *)bucket->data;
1107 bucket = bucket->next;
1109 for (; bucket; bucket = bucket->next) {
1110 element = (MMWFDSinkGstElement *)bucket->data;
1112 if (element && element->gst) {
1113 if (gst_element_link(GST_ELEMENT(prv_element->gst), GST_ELEMENT(element->gst))) {
1114 wfd_sink_debug("linking [%s] to [%s] success",
1115 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT(prv_element->gst))),
1116 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT(element->gst))));
1117 successful_link_count++;
1119 wfd_sink_error("linking [%s] to [%s] failed",
1120 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT(prv_element->gst))),
1121 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT(element->gst))));
1126 prv_element = element;
1129 wfd_sink_debug_fleave();
1131 return successful_link_count;
1135 __mm_wfd_sink_check_state(mm_wfd_sink_t *wfd_sink, MMWFDSinkCommandType cmd)
1137 MMWFDSinkStateType cur_state = MM_WFD_SINK_STATE_NONE;
1139 wfd_sink_debug_fenter();
1141 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
1143 MMWFDSINK_PRINT_STATE(wfd_sink);
1145 cur_state = MMWFDSINK_CURRENT_STATE(wfd_sink);
1148 case MM_WFD_SINK_COMMAND_CREATE:
1149 if (cur_state != MM_WFD_SINK_STATE_NONE)
1152 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_NULL;
1155 case MM_WFD_SINK_COMMAND_PREPARE:
1156 if (cur_state == MM_WFD_SINK_STATE_PREPARED)
1158 else if (cur_state != MM_WFD_SINK_STATE_NULL)
1161 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_PREPARED;
1164 case MM_WFD_SINK_COMMAND_CONNECT:
1165 if (cur_state == MM_WFD_SINK_STATE_CONNECTED)
1167 else if (cur_state != MM_WFD_SINK_STATE_PREPARED)
1170 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_CONNECTED;
1173 case MM_WFD_SINK_COMMAND_START:
1174 if (cur_state == MM_WFD_SINK_STATE_PLAYING)
1176 else if (cur_state != MM_WFD_SINK_STATE_CONNECTED)
1179 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_PLAYING;
1182 case MM_WFD_SINK_COMMAND_PAUSE:
1183 if (cur_state == MM_WFD_SINK_STATE_PAUSED)
1185 else if (cur_state != MM_WFD_SINK_STATE_PLAYING)
1188 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_PAUSED;
1191 case MM_WFD_SINK_COMMAND_RESUME:
1192 if (cur_state == MM_WFD_SINK_STATE_PLAYING)
1194 else if (cur_state != MM_WFD_SINK_STATE_PAUSED)
1197 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_PLAYING;
1200 case MM_WFD_SINK_COMMAND_DISCONNECT:
1201 if (cur_state == MM_WFD_SINK_STATE_NONE ||
1202 cur_state == MM_WFD_SINK_STATE_NULL ||
1203 cur_state == MM_WFD_SINK_STATE_PREPARED ||
1204 cur_state == MM_WFD_SINK_STATE_DISCONNECTED)
1206 else if (cur_state != MM_WFD_SINK_STATE_PLAYING &&
1207 cur_state != MM_WFD_SINK_STATE_CONNECTED &&
1208 cur_state != MM_WFD_SINK_STATE_PAUSED)
1211 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_DISCONNECTED;
1214 case MM_WFD_SINK_COMMAND_UNPREPARE:
1215 if (cur_state == MM_WFD_SINK_STATE_NONE ||
1216 cur_state == MM_WFD_SINK_STATE_NULL)
1219 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_NULL;
1222 case MM_WFD_SINK_COMMAND_DESTROY:
1223 if (cur_state == MM_WFD_SINK_STATE_NONE)
1226 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_NONE;
1233 wfd_sink->cmd = cmd;
1235 wfd_sink_debug_fleave();
1237 return MM_ERROR_NONE;
1240 wfd_sink_debug("already %s state, nothing to do.", MMWFDSINK_STATE_GET_NAME(cur_state));
1241 return MM_ERROR_WFD_NO_OP;
1245 wfd_sink_error("current state[%s] is invalid.", MMWFDSINK_STATE_GET_NAME(cur_state));
1246 return MM_ERROR_WFD_INVALID_STATE;
1249 int __mm_wfd_sink_set_state(mm_wfd_sink_t *wfd_sink, MMWFDSinkStateType state)
1251 wfd_sink_debug_fenter();
1253 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
1255 if (MMWFDSINK_CURRENT_STATE(wfd_sink) == state) {
1256 wfd_sink_error("already state(%s)", MMWFDSINK_STATE_GET_NAME(state));
1257 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_NONE;
1258 return MM_ERROR_NONE;
1261 /* update wi-fi display state */
1262 MMWFDSINK_PREVIOUS_STATE(wfd_sink) = MMWFDSINK_CURRENT_STATE(wfd_sink);
1263 MMWFDSINK_CURRENT_STATE(wfd_sink) = state;
1265 if (MMWFDSINK_CURRENT_STATE(wfd_sink) == MMWFDSINK_PENDING_STATE(wfd_sink))
1266 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_NONE;
1268 /* post state message to application */
1269 MMWFDSINK_POST_MESSAGE(wfd_sink,
1271 MMWFDSINK_CURRENT_STATE(wfd_sink));
1274 MMWFDSINK_PRINT_STATE(wfd_sink);
1276 wfd_sink_debug_fleave();
1278 return MM_ERROR_NONE;
1282 __mm_wfd_sink_set_pipeline_state(mm_wfd_sink_t *wfd_sink, GstState state, gboolean async)
1284 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
1285 GstState cur_state = GST_STATE_VOID_PENDING;
1286 GstState pending_state = GST_STATE_VOID_PENDING;
1288 wfd_sink_debug_fenter();
1290 wfd_sink_return_val_if_fail(wfd_sink &&
1291 wfd_sink->pipeline &&
1292 wfd_sink->pipeline->mainbin &&
1293 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst,
1294 MM_ERROR_WFD_NOT_INITIALIZED);
1296 wfd_sink_return_val_if_fail(state > GST_STATE_VOID_PENDING,
1297 MM_ERROR_WFD_INVALID_ARGUMENT);
1299 wfd_sink_debug("try to set %s state ", gst_element_state_get_name(state));
1301 result = gst_element_set_state(wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst, state);
1302 if (result == GST_STATE_CHANGE_FAILURE) {
1303 wfd_sink_error("fail to set %s state....", gst_element_state_get_name(state));
1304 return MM_ERROR_WFD_INTERNAL;
1308 wfd_sink_debug("wait for changing state is completed ");
1310 result = gst_element_get_state(wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst,
1311 &cur_state, &pending_state, wfd_sink->ini.state_change_timeout * GST_SECOND);
1312 if (result == GST_STATE_CHANGE_FAILURE) {
1313 wfd_sink_error("fail to get state within %d seconds....", wfd_sink->ini.state_change_timeout);
1315 __mm_wfd_sink_dump_pipeline_state(wfd_sink);
1317 return MM_ERROR_WFD_INTERNAL;
1318 } else if (result == GST_STATE_CHANGE_NO_PREROLL) {
1319 wfd_sink_debug("successfully changed state but is not able to provide data yet");
1322 wfd_sink_debug("cur state is %s, pending state is %s",
1323 gst_element_state_get_name(cur_state),
1324 gst_element_state_get_name(pending_state));
1328 wfd_sink_debug_fleave();
1330 return MM_ERROR_NONE;
1334 _mm_wfd_sink_reset_basetime(mm_wfd_sink_t *wfd_sink)
1336 GstClockTime base_time = GST_CLOCK_TIME_NONE;
1339 wfd_sink_debug_fenter();
1341 wfd_sink_return_if_fail(wfd_sink &&
1342 wfd_sink->pipeline &&
1343 wfd_sink->pipeline->mainbin &&
1344 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst);
1345 wfd_sink_return_if_fail(wfd_sink->need_to_reset_basetime);
1348 if (wfd_sink->clock)
1349 base_time = gst_clock_get_time(wfd_sink->clock);
1351 if (GST_CLOCK_TIME_IS_VALID(base_time)) {
1353 wfd_sink_debug("set pipeline base_time as now [%"GST_TIME_FORMAT"]", GST_TIME_ARGS(base_time));
1355 for (i = 0; i < WFD_SINK_M_NUM; i++) {
1356 if (wfd_sink->pipeline->mainbin[i].gst)
1357 gst_element_set_base_time(GST_ELEMENT_CAST(wfd_sink->pipeline->mainbin[i].gst), base_time);
1360 if (wfd_sink->pipeline->v_decodebin) {
1361 for (i = 0; i < WFD_SINK_V_D_NUM; i++) {
1362 if (wfd_sink->pipeline->v_decodebin[i].gst)
1363 gst_element_set_base_time(GST_ELEMENT_CAST(wfd_sink->pipeline->v_decodebin[i].gst), base_time);
1367 if (wfd_sink->pipeline->v_sinkbin) {
1368 for (i = 0; i < WFD_SINK_V_S_NUM; i++) {
1369 if (wfd_sink->pipeline->v_sinkbin[i].gst)
1370 gst_element_set_base_time(GST_ELEMENT_CAST(wfd_sink->pipeline->v_sinkbin[i].gst), base_time);
1374 if (wfd_sink->pipeline->a_decodebin) {
1375 for (i = 0; i < WFD_SINK_A_D_NUM; i++) {
1376 if (wfd_sink->pipeline->a_decodebin[i].gst)
1377 gst_element_set_base_time(GST_ELEMENT_CAST(wfd_sink->pipeline->a_decodebin[i].gst), base_time);
1381 if (wfd_sink->pipeline->a_sinkbin) {
1382 for (i = 0; i < WFD_SINK_A_S_NUM; i++) {
1383 if (wfd_sink->pipeline->a_sinkbin[i].gst)
1384 gst_element_set_base_time(GST_ELEMENT_CAST(wfd_sink->pipeline->a_sinkbin[i].gst), base_time);
1388 wfd_sink->need_to_reset_basetime = FALSE;
1391 wfd_sink_debug_fleave();
1397 __mm_wfd_sink_unprepare_video_pipeline(mm_wfd_sink_t *wfd_sink)
1399 GstElement *pipeline = NULL;
1400 GstElement *v_decodebin = NULL;
1401 GstElement *v_sinkbin = NULL;
1402 GstPad *sinkpad = NULL;
1403 GstPad *srcpad = NULL;
1404 int ret = MM_ERROR_NONE;
1406 wfd_sink_debug_fenter();
1408 wfd_sink_return_val_if_fail(wfd_sink &&
1410 MM_ERROR_WFD_NOT_INITIALIZED);
1412 if (wfd_sink->stream_info.video_stream_info.codec == MM_WFD_SINK_VIDEO_CODEC_NONE) {
1413 wfd_sink_debug("Skip unprepare video pipeline for none audio codec.");
1414 wfd_sink_debug_fleave();
1415 return MM_ERROR_NONE;
1418 PRINT_WFD_REF_COUNT(wfd_sink);
1419 wfd_sink_error("No-error:unprepare video sink bin");
1420 if (wfd_sink->pipeline->v_sinkbin && wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_BIN].gst) {
1421 v_sinkbin = wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_BIN].gst;
1423 if ((pipeline = GST_ELEMENT_CAST(gst_element_get_parent(v_sinkbin)))) {
1424 sinkpad = gst_element_get_static_pad(v_sinkbin, "sink");
1426 wfd_sink_error("failed to get sink pad from %s", GST_ELEMENT_NAME(v_sinkbin));
1430 if (gst_pad_is_linked(sinkpad)) {
1431 srcpad = gst_pad_get_peer(sinkpad);
1433 wfd_sink_error("failed to get peer pad of %s:%s",
1434 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1438 wfd_sink_error("try to unlink %s:%s and %s:%s",
1439 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1440 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1441 if (!gst_pad_unlink(srcpad, sinkpad)) {
1442 wfd_sink_error("failed to unlink %s:%s and %s:%s",
1443 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1444 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1447 gst_object_unref(srcpad);
1450 wfd_sink_debug("video sinkbin's sinkpad is not linked, no need to unlink it");
1452 gst_object_unref(sinkpad);
1455 wfd_sink_error("try to remove %s from %s",
1456 GST_ELEMENT_NAME(v_sinkbin), GST_ELEMENT_NAME(pipeline));
1458 gst_object_ref(v_sinkbin);
1459 if (!gst_bin_remove(GST_BIN(pipeline), GST_ELEMENT(v_sinkbin))) {
1460 wfd_sink_error("failed to remove %s from %s",
1461 GST_ELEMENT_NAME(v_sinkbin), GST_ELEMENT_NAME(pipeline));
1465 gst_object_unref(pipeline);
1469 ret = __mm_wfd_sink_destroy_video_sinkbin(wfd_sink);
1470 if (ret != MM_ERROR_NONE) {
1471 wfd_sink_error("failed to destroy video sinkbin");
1475 PRINT_WFD_REF_COUNT(wfd_sink);
1477 wfd_sink_error("No-error:unprepare video decode bin");
1478 if (wfd_sink->pipeline->v_decodebin && wfd_sink->pipeline->v_decodebin[WFD_SINK_V_D_BIN].gst) {
1479 v_decodebin = wfd_sink->pipeline->v_decodebin[WFD_SINK_V_D_BIN].gst;
1481 if ((pipeline = GST_ELEMENT_CAST(gst_element_get_parent(v_decodebin)))) {
1482 sinkpad = gst_element_get_static_pad(v_decodebin, "sink");
1484 wfd_sink_error("failed to get sink pad from %s", GST_ELEMENT_NAME(v_decodebin));
1488 if (gst_pad_is_linked(sinkpad)) {
1489 srcpad = gst_pad_get_peer(sinkpad);
1491 wfd_sink_error("failed to get peer pad of %s:%s",
1492 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1496 wfd_sink_error("try to unlink %s:%s and %s:%s",
1497 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1498 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1499 if (!gst_pad_unlink(srcpad, sinkpad)) {
1500 wfd_sink_error("failed to unlink %s:%s and %s:%s",
1501 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1502 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1505 gst_object_unref(srcpad);
1508 wfd_sink_debug("video decodebin's sinkpad is not linked, no need to unlink it");
1510 gst_object_unref(sinkpad);
1513 srcpad = gst_element_get_static_pad(v_decodebin, "src");
1515 wfd_sink_error("failed to get src pad from %s", GST_ELEMENT_NAME(v_decodebin));
1519 if (gst_pad_is_linked(srcpad)) {
1520 sinkpad = gst_pad_get_peer(srcpad);
1522 wfd_sink_error("failed to get peer pad of %s:%s",
1523 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad));
1527 wfd_sink_error("try to unlink %s:%s and %s:%s",
1528 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1529 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1530 if (!gst_pad_unlink(srcpad, sinkpad)) {
1531 wfd_sink_error("failed to unlink %s:%s and %s:%s",
1532 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1533 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1536 gst_object_unref(sinkpad);
1539 wfd_sink_debug("video decodebin's srcpad is not linked, no need to unlink it");
1541 gst_object_unref(srcpad);
1544 wfd_sink_error("try to remove %s from %s",
1545 GST_ELEMENT_NAME(v_decodebin), GST_ELEMENT_NAME(pipeline));
1546 gst_object_ref(v_decodebin);
1547 if (!gst_bin_remove(GST_BIN(pipeline), GST_ELEMENT(v_decodebin))) {
1548 wfd_sink_error("failed to remove %s from %s",
1549 GST_ELEMENT_NAME(v_decodebin), GST_ELEMENT_NAME(pipeline));
1553 gst_object_unref(pipeline);
1557 ret = __mm_wfd_sink_destroy_video_decodebin(wfd_sink);
1558 if (ret != MM_ERROR_NONE) {
1559 wfd_sink_error("failed to destroy video decodebin");
1563 PRINT_WFD_REF_COUNT(wfd_sink);
1565 wfd_sink_debug_fleave();
1572 gst_object_unref(pipeline);
1577 gst_object_unref(sinkpad);
1582 gst_object_unref(srcpad);
1586 /* need to notify to app */
1587 MMWFDSINK_POST_MESSAGE(wfd_sink,
1588 MM_ERROR_WFD_INTERNAL,
1589 MMWFDSINK_CURRENT_STATE(wfd_sink));
1591 return MM_ERROR_WFD_INTERNAL;
1595 __mm_wfd_sink_prepare_video_pipeline(mm_wfd_sink_t *wfd_sink, GstPad **pad)
1597 GstElement *pipeline = NULL;
1598 GstElement *v_decodebin = NULL;
1599 GstElement *v_sinkbin = NULL;
1600 GstPad *srcpad = NULL;
1601 GstPad *sinkpad = NULL;
1603 wfd_sink_debug_fenter();
1605 wfd_sink_return_val_if_fail(wfd_sink &&
1606 wfd_sink->pipeline &&
1607 wfd_sink->pipeline->mainbin &&
1608 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst,
1609 MM_ERROR_WFD_NOT_INITIALIZED);
1611 if (wfd_sink->stream_info.video_stream_info.codec == MM_WFD_SINK_VIDEO_CODEC_NONE) {
1612 wfd_sink_debug("Skip prepare video pipeline for none audio codec");
1613 wfd_sink_debug_fleave();
1614 return MM_ERROR_NONE;
1617 /* check video decodebin is linked */
1618 if (!wfd_sink->video_decodebin_is_linked) {
1619 /* check video decodebin is created */
1620 if (wfd_sink->pipeline->v_decodebin == NULL) {
1621 if (MM_ERROR_NONE != __mm_wfd_sink_create_video_decodebin(wfd_sink)) {
1622 wfd_sink_error("failed to create video decodebin....");
1627 if (MM_ERROR_NONE != __mm_wfd_sink_link_video_decodebin(wfd_sink)) {
1628 wfd_sink_error("failed to link video decodebin.....");
1633 /* check video sinkbin is created */
1634 if (wfd_sink->pipeline->v_sinkbin == NULL) {
1635 if (MM_ERROR_NONE != __mm_wfd_sink_create_video_sinkbin(wfd_sink)) {
1636 wfd_sink_error("failed to create video sinkbin....");
1640 /* add video decodebin to pipeline */
1641 if (wfd_sink->pipeline->v_decodebin && wfd_sink->pipeline->v_decodebin[WFD_SINK_V_D_BIN].gst) {
1642 v_decodebin = wfd_sink->pipeline->v_decodebin[WFD_SINK_V_D_BIN].gst;
1644 pipeline = GST_ELEMENT_CAST(gst_element_get_parent(v_decodebin));
1646 pipeline = wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst;
1648 if (GST_STATE(v_decodebin) <= GST_STATE_NULL) {
1649 wfd_sink_debug("need to prepare %s", GST_ELEMENT_NAME(v_decodebin));
1650 if (GST_STATE_CHANGE_FAILURE == gst_element_set_state(v_decodebin, GST_STATE_READY)) {
1651 wfd_sink_error("failed to set state(READY) to video decodebin");
1656 wfd_sink_debug("try to add %s to %s",
1657 GST_ELEMENT_NAME(v_decodebin), GST_ELEMENT_NAME(pipeline));
1658 if (!gst_bin_add(GST_BIN(pipeline), GST_ELEMENT(v_decodebin))) {
1659 wfd_sink_error("failed to add %s to %s",
1660 GST_ELEMENT_NAME(v_decodebin), GST_ELEMENT_NAME(pipeline));
1664 wfd_sink_debug("need to sync state %s with its parent", GST_ELEMENT_NAME(v_decodebin));
1665 if (!gst_element_sync_state_with_parent(GST_ELEMENT(v_decodebin))) {
1666 wfd_sink_error("failed to sync %s state with parent", GST_ELEMENT_NAME(v_decodebin));
1670 wfd_sink_debug("%s is already added to %s",
1671 GST_ELEMENT_NAME(v_decodebin), GST_ELEMENT_NAME(pipeline));
1672 gst_object_unref(pipeline);
1676 wfd_sink_warning("going on without video decodebin....");
1679 /* add video sinkbin to pipeline */
1680 if (wfd_sink->pipeline->v_sinkbin && wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_BIN].gst) {
1681 v_sinkbin = wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_BIN].gst;
1683 pipeline = GST_ELEMENT_CAST(gst_element_get_parent(v_sinkbin));
1685 pipeline = wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst;
1687 /* prepare video sinkbin before adding */
1688 if (GST_STATE(v_sinkbin) <= GST_STATE_NULL) {
1689 wfd_sink_debug("need to prepare %s", GST_ELEMENT_NAME(v_sinkbin));
1690 if (GST_STATE_CHANGE_FAILURE == gst_element_set_state(v_sinkbin, GST_STATE_READY)) {
1691 wfd_sink_error("failed to set state(READY) to video sinkbin");
1695 /* add video sinkbin to pipeline */
1696 wfd_sink_debug("try to add %s to %s",
1697 GST_ELEMENT_NAME(v_sinkbin), GST_ELEMENT_NAME(pipeline));
1698 if (!gst_bin_add(GST_BIN(pipeline), GST_ELEMENT(v_sinkbin))) {
1699 wfd_sink_error("failed to add %s to %s",
1700 GST_ELEMENT_NAME(v_sinkbin), GST_ELEMENT_NAME(pipeline));
1704 /* sync state with parent */
1705 wfd_sink_debug("need to sync state %s with its parent", GST_ELEMENT_NAME(v_sinkbin));
1706 if (!gst_element_sync_state_with_parent(GST_ELEMENT(v_sinkbin))) {
1707 wfd_sink_error("failed to sync %s state with parent", GST_ELEMENT_NAME(v_sinkbin));
1711 wfd_sink_debug("%s is already added to %s",
1712 GST_ELEMENT_NAME(v_sinkbin), GST_ELEMENT_NAME(pipeline));
1713 gst_object_unref(pipeline);
1717 wfd_sink_warning("going on without video sinkbin....");
1721 /* link video decodebin and sinkbin */
1722 if (wfd_sink->pipeline->v_decodebin && wfd_sink->pipeline->v_decodebin[WFD_SINK_V_D_BIN].gst) {
1723 v_decodebin = wfd_sink->pipeline->v_decodebin[WFD_SINK_V_D_BIN].gst;
1726 *pad = gst_element_get_static_pad(v_decodebin, "sink");
1728 if (wfd_sink->pipeline->v_sinkbin && wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_BIN].gst) {
1730 v_sinkbin = wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_BIN].gst;
1732 srcpad = gst_element_get_static_pad(v_decodebin, "src");
1734 wfd_sink_error("faied to get srcpad from %s", GST_ELEMENT_NAME(v_decodebin));
1738 if (!gst_pad_is_linked(srcpad)) {
1739 sinkpad = gst_element_get_static_pad(v_sinkbin, "sink");
1741 wfd_sink_error("faied to get sinkpad from %s", GST_ELEMENT_NAME(v_sinkbin));
1745 wfd_sink_debug("try to link %s:%s and %s:%s",
1746 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1747 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1748 if (GST_PAD_LINK_OK != gst_pad_link_full(srcpad, sinkpad, GST_PAD_LINK_CHECK_NOTHING)) {
1749 wfd_sink_error("failed to link %s:%s and %s:%s",
1750 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1751 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1754 gst_object_unref(sinkpad);
1757 gst_object_unref(srcpad);
1760 } else if (wfd_sink->pipeline->v_sinkbin && wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_BIN].gst) {
1761 v_sinkbin = wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_BIN].gst;
1763 *pad = gst_element_get_static_pad(v_sinkbin, "sink");
1766 wfd_sink_debug_fleave();
1768 return MM_ERROR_NONE;
1772 if (sinkpad != NULL) {
1773 gst_object_unref(sinkpad);
1777 if (srcpad != NULL) {
1778 gst_object_unref(srcpad);
1782 /* need to notify to app */
1783 MMWFDSINK_POST_MESSAGE(wfd_sink,
1784 MM_ERROR_WFD_INTERNAL,
1785 MMWFDSINK_CURRENT_STATE(wfd_sink));
1787 return MM_ERROR_WFD_INTERNAL;
1791 __mm_wfd_sink_unprepare_audio_pipeline(mm_wfd_sink_t *wfd_sink)
1793 GstElement *pipeline = NULL;
1794 GstElement *a_decodebin = NULL;
1795 GstElement *a_sinkbin = NULL;
1796 GstPad *sinkpad = NULL;
1797 GstPad *srcpad = NULL;
1798 int ret = MM_ERROR_NONE;
1800 wfd_sink_debug_fenter();
1802 wfd_sink_return_val_if_fail(wfd_sink &&
1804 MM_ERROR_WFD_NOT_INITIALIZED);
1806 if (wfd_sink->stream_info.audio_stream_info.codec == MM_WFD_SINK_AUDIO_CODEC_NONE) {
1807 wfd_sink_debug("Skip unprepare audio pipeline for none audio codec.");
1808 wfd_sink_debug_fleave();
1809 return MM_ERROR_NONE;
1812 wfd_sink_error("No-error:unprepare audio sink bin");
1813 PRINT_WFD_REF_COUNT(wfd_sink);
1815 if (wfd_sink->pipeline->a_sinkbin && wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_BIN].gst) {
1816 a_sinkbin = wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_BIN].gst;
1818 if ((pipeline = GST_ELEMENT_CAST(gst_element_get_parent(a_sinkbin)))) {
1819 sinkpad = gst_element_get_static_pad(a_sinkbin, "sink");
1821 wfd_sink_error("failed to get sink pad from %s", GST_ELEMENT_NAME(a_sinkbin));
1825 if (gst_pad_is_linked(sinkpad)) {
1826 srcpad = gst_pad_get_peer(sinkpad);
1828 wfd_sink_error("failed to get peer pad of %s:%s",
1829 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1833 wfd_sink_error("try to unlink %s:%s and %s:%s",
1834 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1835 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1836 if (!gst_pad_unlink(srcpad, sinkpad)) {
1837 wfd_sink_error("failed to unlink %s:%s and %s:%s",
1838 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1839 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1842 gst_object_unref(srcpad);
1845 wfd_sink_debug("audio sinkbin's sinkpad is not linked, no need to unlink it");
1847 gst_object_unref(sinkpad);
1850 wfd_sink_error("try to remove %s from %s", GST_ELEMENT_NAME(a_sinkbin), GST_ELEMENT_NAME(pipeline));
1852 gst_object_ref(a_sinkbin);
1853 if (!gst_bin_remove(GST_BIN(pipeline), GST_ELEMENT(a_sinkbin))) {
1854 wfd_sink_error("failed to remove %s from %s",
1855 GST_ELEMENT_NAME(a_sinkbin), GST_ELEMENT_NAME(pipeline));
1859 gst_object_unref(pipeline);
1863 ret = __mm_wfd_sink_destroy_audio_sinkbin(wfd_sink);
1864 if (ret != MM_ERROR_NONE) {
1865 wfd_sink_error("failed to destroy audio sinkbin");
1869 PRINT_WFD_REF_COUNT(wfd_sink);
1871 wfd_sink_error("No-error:unprepare audio decode bin");
1872 if (wfd_sink->pipeline->a_decodebin && wfd_sink->pipeline->a_decodebin[WFD_SINK_A_D_BIN].gst) {
1873 a_decodebin = wfd_sink->pipeline->a_decodebin[WFD_SINK_A_D_BIN].gst;
1875 if ((pipeline = GST_ELEMENT_CAST(gst_element_get_parent(a_decodebin)))) {
1876 sinkpad = gst_element_get_static_pad(a_decodebin, "sink");
1878 wfd_sink_error("failed to get sink pad from %s", GST_ELEMENT_NAME(a_decodebin));
1882 if (gst_pad_is_linked(sinkpad)) {
1883 srcpad = gst_pad_get_peer(sinkpad);
1885 wfd_sink_error("failed to get peer pad of %s:%s",
1886 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1890 wfd_sink_error("try to unlink %s:%s and %s:%s",
1891 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1892 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1893 if (!gst_pad_unlink(srcpad, sinkpad)) {
1894 wfd_sink_error("failed to unlink %s:%s and %s:%s",
1895 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1896 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1899 gst_object_unref(srcpad);
1902 wfd_sink_debug("audio decodebin's sinkpad is not linked, no need to unlink it");
1904 gst_object_unref(sinkpad);
1907 srcpad = gst_element_get_static_pad(a_decodebin, "src");
1909 wfd_sink_error("failed to get src pad from %s", GST_ELEMENT_NAME(a_decodebin));
1913 if (gst_pad_is_linked(srcpad)) {
1914 sinkpad = gst_pad_get_peer(srcpad);
1916 wfd_sink_error("failed to get peer pad of %s:%s",
1917 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad));
1918 return MM_ERROR_WFD_INTERNAL;
1921 wfd_sink_error("try to unlink %s:%s and %s:%s",
1922 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1923 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1924 if (!gst_pad_unlink(srcpad, sinkpad)) {
1925 wfd_sink_error("failed to unlink %s:%s and %s:%s",
1926 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1927 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1928 return MM_ERROR_WFD_INTERNAL;
1930 gst_object_unref(sinkpad);
1933 wfd_sink_debug("audio decodebin's srcpad is not linked, no need to unlink it");
1935 gst_object_unref(srcpad);
1938 wfd_sink_error("try to remove %s from %s",
1939 GST_ELEMENT_NAME(a_decodebin), GST_ELEMENT_NAME(pipeline));
1940 gst_object_ref(a_decodebin);
1941 if (!gst_bin_remove(GST_BIN(pipeline), GST_ELEMENT(a_decodebin))) {
1942 wfd_sink_error("failed to remove %s from %s",
1943 GST_ELEMENT_NAME(a_decodebin), GST_ELEMENT_NAME(pipeline));
1947 gst_object_unref(pipeline);
1951 ret = __mm_wfd_sink_destroy_audio_decodebin(wfd_sink);
1952 if (ret != MM_ERROR_NONE) {
1953 wfd_sink_error("failed to destroy audio decodebin");
1957 PRINT_WFD_REF_COUNT(wfd_sink);
1959 wfd_sink_debug_fleave();
1966 gst_object_unref(pipeline);
1970 gst_object_unref(sinkpad);
1975 gst_object_unref(srcpad);
1979 /* need to notify to app */
1980 MMWFDSINK_POST_MESSAGE(wfd_sink,
1981 MM_ERROR_WFD_INTERNAL,
1982 MMWFDSINK_CURRENT_STATE(wfd_sink));
1984 return MM_ERROR_WFD_INTERNAL;
1988 __mm_wfd_sink_prepare_audio_pipeline(mm_wfd_sink_t *wfd_sink, GstPad **pad)
1990 GstElement *pipeline = NULL;
1991 GstElement *a_decodebin = NULL;
1992 GstElement *a_sinkbin = NULL;
1993 GstPad *srcpad = NULL;
1994 GstPad *sinkpad = NULL;
1996 wfd_sink_debug_fenter();
1998 wfd_sink_return_val_if_fail(wfd_sink &&
1999 wfd_sink->pipeline &&
2000 wfd_sink->pipeline->mainbin &&
2001 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst,
2002 MM_ERROR_WFD_NOT_INITIALIZED);
2004 if (wfd_sink->stream_info.audio_stream_info.codec == MM_WFD_SINK_AUDIO_CODEC_NONE) {
2005 wfd_sink_debug("Skip prepare audio pipeline for none audio codec");
2006 wfd_sink_debug_fleave();
2007 return MM_ERROR_NONE;
2010 /* check audio decodebin is linked */
2011 if (!wfd_sink->audio_decodebin_is_linked) {
2012 /* check audio decodebin is created */
2013 if (wfd_sink->pipeline->a_decodebin == NULL) {
2014 if (MM_ERROR_NONE != __mm_wfd_sink_create_audio_decodebin(wfd_sink)) {
2015 wfd_sink_error("failed to create audio decodebin....");
2020 if (MM_ERROR_NONE != __mm_wfd_sink_link_audio_decodebin(wfd_sink)) {
2021 wfd_sink_error("failed to link audio decodebin.....");
2026 /* check audio sinkbin is created */
2027 if (wfd_sink->pipeline->a_sinkbin == NULL) {
2028 if (MM_ERROR_NONE != __mm_wfd_sink_create_audio_sinkbin(wfd_sink)) {
2029 wfd_sink_error("failed to create audio sinkbin....");
2034 /* add audio decodebin to pipeline */
2035 if (wfd_sink->pipeline->a_decodebin && wfd_sink->pipeline->a_decodebin[WFD_SINK_A_D_BIN].gst) {
2036 a_decodebin = wfd_sink->pipeline->a_decodebin[WFD_SINK_A_D_BIN].gst;
2038 pipeline = GST_ELEMENT_CAST(gst_element_get_parent(a_decodebin));
2040 pipeline = wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst;
2042 if (GST_STATE(a_decodebin) <= GST_STATE_NULL) {
2043 wfd_sink_debug("need to prepare %s", GST_ELEMENT_NAME(a_decodebin));
2044 if (GST_STATE_CHANGE_FAILURE == gst_element_set_state(a_decodebin, GST_STATE_READY)) {
2045 wfd_sink_error("failed to set state(READY) to audio decodebin");
2050 wfd_sink_debug("try to add %s to %s",
2051 GST_ELEMENT_NAME(a_decodebin), GST_ELEMENT_NAME(pipeline));
2052 if (!gst_bin_add(GST_BIN(pipeline), GST_ELEMENT(a_decodebin))) {
2053 wfd_sink_error("failed to add %s to %s",
2054 GST_ELEMENT_NAME(a_decodebin), GST_ELEMENT_NAME(pipeline));
2058 wfd_sink_debug("need to sync state %s with its parent", GST_ELEMENT_NAME(a_decodebin));
2059 if (!gst_element_sync_state_with_parent(GST_ELEMENT(a_decodebin))) {
2060 wfd_sink_error("failed to sync %s state with parent", GST_ELEMENT_NAME(a_decodebin));
2064 wfd_sink_debug("%s is already added to %s",
2065 GST_ELEMENT_NAME(a_decodebin), GST_ELEMENT_NAME(pipeline));
2066 gst_object_unref(pipeline);
2070 wfd_sink_warning("going on without audio decodebin....");
2073 /* add audio sinkbin to pipeline */
2074 if (wfd_sink->pipeline->a_sinkbin && wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_BIN].gst) {
2075 a_sinkbin = wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_BIN].gst;
2077 pipeline = GST_ELEMENT_CAST(gst_element_get_parent(a_sinkbin));
2079 pipeline = wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst;
2081 /* prepare audio sinkbin before adding */
2082 if (GST_STATE(a_sinkbin) <= GST_STATE_NULL) {
2083 wfd_sink_debug("need to prepare %s", GST_ELEMENT_NAME(a_sinkbin));
2084 if (GST_STATE_CHANGE_FAILURE == gst_element_set_state(a_sinkbin, GST_STATE_READY)) {
2085 wfd_sink_error("failed to set state(READY) to audio sinkbin");
2090 /* add audio sinkbin to pipeline */
2091 wfd_sink_debug("try to add %s to %s",
2092 GST_ELEMENT_NAME(a_sinkbin), GST_ELEMENT_NAME(pipeline));
2093 if (!gst_bin_add(GST_BIN(pipeline), GST_ELEMENT(a_sinkbin))) {
2094 wfd_sink_error("failed to add %s to %s",
2095 GST_ELEMENT_NAME(a_sinkbin), GST_ELEMENT_NAME(pipeline));
2099 /* sync state with parent */
2100 wfd_sink_debug("need to sync state %s with its parent", GST_ELEMENT_NAME(a_sinkbin));
2101 if (!gst_element_sync_state_with_parent(GST_ELEMENT(a_sinkbin))) {
2102 wfd_sink_error("failed to sync %s state with parent", GST_ELEMENT_NAME(a_sinkbin));
2106 wfd_sink_debug("%s is already added to %s",
2107 GST_ELEMENT_NAME(a_sinkbin), GST_ELEMENT_NAME(pipeline));
2108 gst_object_unref(pipeline);
2112 wfd_sink_warning("going on without audio sinkbin....");
2116 /* link audio decodebin and sinkbin */
2117 if (wfd_sink->pipeline->a_decodebin && wfd_sink->pipeline->a_decodebin[WFD_SINK_A_D_BIN].gst) {
2118 a_decodebin = wfd_sink->pipeline->a_decodebin[WFD_SINK_A_D_BIN].gst;
2121 *pad = gst_element_get_static_pad(a_decodebin, "sink");
2123 if (wfd_sink->pipeline->a_sinkbin && wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_BIN].gst) {
2125 a_sinkbin = wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_BIN].gst;
2127 srcpad = gst_element_get_static_pad(a_decodebin, "src");
2129 wfd_sink_error("faied to get srcpad from %s", GST_ELEMENT_NAME(a_decodebin));
2133 if (!gst_pad_is_linked(srcpad)) {
2134 sinkpad = gst_element_get_static_pad(a_sinkbin, "sink");
2136 wfd_sink_error("faied to get sinkpad from %s", GST_ELEMENT_NAME(a_sinkbin));
2140 wfd_sink_debug("try to link %s:%s and %s:%s",
2141 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
2142 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
2143 if (GST_PAD_LINK_OK != gst_pad_link_full(srcpad, sinkpad, GST_PAD_LINK_CHECK_NOTHING)) {
2144 wfd_sink_error("failed to link %s:%s and %s:%s",
2145 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
2146 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
2150 gst_object_unref(sinkpad);
2153 gst_object_unref(srcpad);
2156 } else if (wfd_sink->pipeline->a_sinkbin && wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_BIN].gst) {
2157 a_sinkbin = wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_BIN].gst;
2159 *pad = gst_element_get_static_pad(a_sinkbin, "sink");
2162 wfd_sink_debug_fleave();
2164 return MM_ERROR_NONE;
2169 gst_object_unref(pipeline);
2174 gst_object_unref(sinkpad);
2179 gst_object_unref(srcpad);
2183 /* need to notify to app */
2184 MMWFDSINK_POST_MESSAGE(wfd_sink,
2185 MM_ERROR_WFD_INTERNAL,
2186 MMWFDSINK_CURRENT_STATE(wfd_sink));
2188 return MM_ERROR_WFD_INTERNAL;
2191 #define COMPENSATION_CRETERIA_VALUE 1000000 /* 1 msec */
2192 #define COMPENSATION_CHECK_PERIOD (30*GST_SECOND) /* 30 sec */
2194 static GstPadProbeReturn
2195 _mm_wfd_sink_check_running_time(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
2197 mm_wfd_sink_t *wfd_sink = (mm_wfd_sink_t *)u_data;
2198 GstClockTime current_time = GST_CLOCK_TIME_NONE;
2199 GstClockTime start_time = GST_CLOCK_TIME_NONE;
2200 GstClockTime running_time = GST_CLOCK_TIME_NONE;
2201 GstClockTime base_time = GST_CLOCK_TIME_NONE;
2202 GstClockTime render_time = GST_CLOCK_TIME_NONE;
2203 GstClockTimeDiff diff = GST_CLOCK_TIME_NONE;
2204 GstBuffer *buffer = NULL;
2205 gint64 ts_offset = 0LL;
2207 wfd_sink_return_val_if_fail(info, FALSE);
2208 wfd_sink_return_val_if_fail(wfd_sink &&
2209 wfd_sink->pipeline &&
2210 wfd_sink->pipeline->mainbin &&
2211 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst,
2212 GST_PAD_PROBE_DROP);
2214 if (!wfd_sink->clock) {
2215 wfd_sink_warning("pipeline did not select clock, yet");
2216 return GST_PAD_PROBE_OK;
2219 if (wfd_sink->need_to_reset_basetime)
2220 _mm_wfd_sink_reset_basetime(wfd_sink);
2222 /* calculate current runninig time */
2223 current_time = gst_clock_get_time(wfd_sink->clock);
2224 if (g_strrstr(GST_OBJECT_NAME(pad), "video"))
2225 base_time = gst_element_get_base_time(wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_BIN].gst);
2226 else if (g_strrstr(GST_OBJECT_NAME(pad), "audio"))
2227 base_time = gst_element_get_base_time(wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_BIN].gst);
2228 start_time = gst_element_get_start_time(wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst);
2229 if (GST_CLOCK_TIME_IS_VALID(current_time) &&
2230 GST_CLOCK_TIME_IS_VALID(start_time) &&
2231 GST_CLOCK_TIME_IS_VALID(base_time)) {
2232 running_time = current_time - (start_time + base_time);
2234 wfd_sink_debug("current time %"GST_TIME_FORMAT", start time %"GST_TIME_FORMAT
2235 " base time %"GST_TIME_FORMAT"", GST_TIME_ARGS(current_time),
2236 GST_TIME_ARGS(start_time), GST_TIME_ARGS(base_time));
2237 return GST_PAD_PROBE_OK;
2240 /* calculate this buffer rendering time */
2241 buffer = gst_pad_probe_info_get_buffer(info);
2242 if (!GST_BUFFER_TIMESTAMP_IS_VALID(buffer)) {
2243 wfd_sink_warning("buffer timestamp is invalid.");
2244 return GST_PAD_PROBE_OK;
2247 if (g_strrstr(GST_OBJECT_NAME(pad), "audio")) {
2248 if (wfd_sink->pipeline && wfd_sink->pipeline->a_sinkbin && wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_SINK].gst)
2249 g_object_get(G_OBJECT(wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_SINK].gst), "ts-offset", &ts_offset, NULL);
2250 } else if (g_strrstr(GST_OBJECT_NAME(pad), "video")) {
2251 if (wfd_sink->pipeline && wfd_sink->pipeline->v_sinkbin && wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_SINK].gst)
2252 g_object_get(G_OBJECT(wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_SINK].gst), "ts-offset", &ts_offset, NULL);
2255 render_time = GST_BUFFER_TIMESTAMP(buffer);
2256 render_time += ts_offset;
2258 /* chekc this buffer could be rendered or not */
2259 if (GST_CLOCK_TIME_IS_VALID(running_time) && GST_CLOCK_TIME_IS_VALID(render_time)) {
2260 diff = GST_CLOCK_DIFF(running_time, render_time);
2262 /* this buffer could be NOT rendered */
2263 wfd_sink_debug("%s : diff time : -%" GST_TIME_FORMAT "",
2264 GST_STR_NULL((GST_OBJECT_NAME(pad))),
2265 GST_TIME_ARGS(GST_CLOCK_DIFF(render_time, running_time)));
2267 /* this buffer could be rendered */
2268 /*wfd_sink_debug ("%s :diff time : %" GST_TIME_FORMAT "", */
2269 /* GST_STR_NULL((GST_OBJECT_NAME(pad))), */
2270 /* GST_TIME_ARGS(diff)); */
2274 /* update buffer count and gap */
2275 if (g_strrstr(GST_OBJECT_NAME(pad), "video")) {
2276 wfd_sink->video_buffer_count++;
2277 wfd_sink->video_accumulated_gap += diff;
2278 } else if (g_strrstr(GST_OBJECT_NAME(pad), "audio")) {
2279 wfd_sink->audio_buffer_count++;
2280 wfd_sink->audio_accumulated_gap += diff;
2282 wfd_sink_warning("invalid buffer type.. ");
2283 return GST_PAD_PROBE_DROP;
2286 if (GST_CLOCK_TIME_IS_VALID(wfd_sink->last_buffer_timestamp)) {
2287 /* fisrt 60sec, just calculate the gap between source device and sink device */
2288 if (GST_BUFFER_TIMESTAMP(buffer) < 60 * GST_SECOND)
2289 return GST_PAD_PROBE_OK;
2291 /* every 10sec, calculate the gap between source device and sink device */
2292 if (GST_CLOCK_DIFF(wfd_sink->last_buffer_timestamp, GST_BUFFER_TIMESTAMP(buffer))
2293 > COMPENSATION_CHECK_PERIOD) {
2294 gint64 audio_avgrage_gap = 0LL;
2295 gint64 video_avgrage_gap = 0LL;
2296 gint64 audio_avgrage_gap_diff = 0LL;
2297 gint64 video_avgrage_gap_diff = 0LL;
2298 gboolean video_minus_compensation = FALSE;
2299 gboolean audio_minus_compensation = FALSE;
2300 gint64 avgrage_gap_diff = 0LL;
2301 gboolean minus_compensation = FALSE;
2304 if (wfd_sink->video_buffer_count > 0) {
2305 video_avgrage_gap = wfd_sink->video_accumulated_gap / wfd_sink->video_buffer_count;
2307 if (wfd_sink->video_average_gap != 0) {
2308 if (video_avgrage_gap > wfd_sink->video_average_gap) {
2309 video_avgrage_gap_diff = video_avgrage_gap - wfd_sink->video_average_gap;
2310 video_minus_compensation = TRUE;
2312 video_avgrage_gap_diff = wfd_sink->video_average_gap - video_avgrage_gap;
2313 video_minus_compensation = FALSE;
2316 wfd_sink_debug("first update video average gap(%"G_GINT64_FORMAT"d) ", video_avgrage_gap);
2317 wfd_sink->video_average_gap = video_avgrage_gap;
2320 wfd_sink_debug("there is no video buffer flow during %"GST_TIME_FORMAT
2321 " ~ %" GST_TIME_FORMAT"",
2322 GST_TIME_ARGS(wfd_sink->last_buffer_timestamp),
2323 GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(buffer)));
2327 if (wfd_sink->audio_buffer_count > 0) {
2328 audio_avgrage_gap = wfd_sink->audio_accumulated_gap / wfd_sink->audio_buffer_count;
2330 if (wfd_sink->audio_average_gap != 0) {
2331 if (audio_avgrage_gap > wfd_sink->audio_average_gap) {
2332 audio_avgrage_gap_diff = audio_avgrage_gap - wfd_sink->audio_average_gap;
2333 audio_minus_compensation = TRUE;
2335 audio_avgrage_gap_diff = wfd_sink->audio_average_gap - audio_avgrage_gap;
2336 audio_minus_compensation = FALSE;
2339 wfd_sink_debug("first update audio average gap(%"G_GINT64_FORMAT"d) ", audio_avgrage_gap);
2340 wfd_sink->audio_average_gap = audio_avgrage_gap;
2343 wfd_sink_debug("there is no audio buffer flow during %"GST_TIME_FORMAT
2344 " ~ %" GST_TIME_FORMAT"",
2345 GST_TIME_ARGS(wfd_sink->last_buffer_timestamp),
2346 GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(buffer)));
2349 /* selecet average_gap_diff between video and audio */
2350 /* which makes no buffer drop in the sink elements */
2351 if (video_avgrage_gap_diff && audio_avgrage_gap_diff) {
2352 if (!video_minus_compensation && !audio_minus_compensation) {
2353 minus_compensation = FALSE;
2354 if (video_avgrage_gap_diff > audio_avgrage_gap_diff)
2355 avgrage_gap_diff = video_avgrage_gap_diff;
2357 avgrage_gap_diff = audio_avgrage_gap_diff;
2358 } else if (video_minus_compensation && audio_minus_compensation) {
2359 minus_compensation = TRUE;
2360 if (video_avgrage_gap_diff > audio_avgrage_gap_diff)
2361 avgrage_gap_diff = audio_avgrage_gap_diff;
2363 avgrage_gap_diff = video_avgrage_gap_diff;
2365 minus_compensation = FALSE;
2366 if (!video_minus_compensation)
2367 avgrage_gap_diff = video_avgrage_gap_diff;
2369 avgrage_gap_diff = audio_avgrage_gap_diff;
2371 } else if (video_avgrage_gap_diff) {
2372 minus_compensation = video_minus_compensation;
2373 avgrage_gap_diff = video_avgrage_gap_diff;
2374 } else if (audio_avgrage_gap_diff) {
2375 minus_compensation = audio_minus_compensation;
2376 avgrage_gap_diff = audio_avgrage_gap_diff;
2379 wfd_sink_debug("average diff gap difference beween audio:%s%"G_GINT64_FORMAT"d and video:%s%"G_GINT64_FORMAT"d ",
2380 audio_minus_compensation ? "-" : "", audio_avgrage_gap_diff,
2381 video_minus_compensation ? "-" : "", video_avgrage_gap_diff);
2384 /* if calculated gap diff is larger than 1ms. need to compensate buffer timestamp */
2385 if (avgrage_gap_diff >= COMPENSATION_CRETERIA_VALUE) {
2386 if (minus_compensation)
2387 ts_offset -= avgrage_gap_diff;
2389 ts_offset += avgrage_gap_diff;
2391 wfd_sink_debug("do timestamp compensation : %s%"G_GINT64_FORMAT"d (ts-offset : %"
2392 GST_TIME_FORMAT") at(%" GST_TIME_FORMAT")",
2393 minus_compensation ? "-" : "", avgrage_gap_diff,
2394 GST_TIME_ARGS(ts_offset), GST_TIME_ARGS(running_time));
2396 if (wfd_sink->pipeline && wfd_sink->pipeline->a_sinkbin && wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_SINK].gst)
2397 g_object_set(G_OBJECT(wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_SINK].gst), "ts-offset", (gint64)ts_offset, NULL);
2398 if (wfd_sink->pipeline && wfd_sink->pipeline->v_sinkbin && wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_SINK].gst)
2399 g_object_set(G_OBJECT(wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_SINK].gst), "ts-offset", (gint64)ts_offset, NULL);
2401 wfd_sink_debug("don't need to do timestamp compensation : %s%"G_GINT64_FORMAT"d (ts-offset : %"GST_TIME_FORMAT ")",
2402 minus_compensation ? "-" : "", avgrage_gap_diff, GST_TIME_ARGS(ts_offset));
2406 wfd_sink->video_buffer_count = 0;
2407 wfd_sink->video_accumulated_gap = 0LL;
2408 wfd_sink->audio_buffer_count = 0;
2409 wfd_sink->audio_accumulated_gap = 0LL;
2410 wfd_sink->last_buffer_timestamp = GST_BUFFER_TIMESTAMP(buffer);
2413 wfd_sink_debug("first update last buffer timestamp :%" GST_TIME_FORMAT,
2414 GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(buffer)));
2415 wfd_sink->last_buffer_timestamp = GST_BUFFER_TIMESTAMP(buffer);
2418 return GST_PAD_PROBE_OK;
2423 __mm_wfd_sink_demux_pad_added(GstElement *demux, GstPad *pad, gpointer data)
2425 mm_wfd_sink_t *wfd_sink = (mm_wfd_sink_t *)data;
2427 GstElement *pipeline = NULL;
2428 GstElement *valve = NULL;
2429 GstPad *sinkpad = NULL;
2430 GstPad *srcpad = NULL;
2432 wfd_sink_debug_fenter();
2434 wfd_sink_return_if_fail(wfd_sink &&
2435 wfd_sink->pipeline &&
2436 wfd_sink->pipeline->mainbin &&
2437 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst);
2439 name = gst_pad_get_name(pad);
2441 wfd_sink_error("fail to get pad");
2445 wfd_sink_debug("Mux pad added, video_codec=%d, audio_codec=%d, name[0] = %c",
2446 wfd_sink->stream_info.video_stream_info.codec,
2447 wfd_sink->stream_info.audio_stream_info.codec,
2450 //In case of none vieo codec, we don't add video pad
2451 if (wfd_sink->stream_info.video_stream_info.codec == MM_WFD_SINK_VIDEO_CODEC_NONE && name[0] == 'v') {
2452 wfd_sink_error("Skip video pad add for none video codec");
2457 //In case of none audio codec, we don't add audio pad
2458 if (wfd_sink->stream_info.audio_stream_info.codec == MM_WFD_SINK_AUDIO_CODEC_NONE && name[0] == 'a') {
2459 wfd_sink_error("Skip audio pad add for none audio codec");
2464 pipeline = wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst;
2466 /* take srcpad from demuxer added pad */
2467 srcpad = gst_object_ref(pad);
2469 if (name[0] == 'v') {
2470 if (wfd_sink->pipeline->mainbin[WFD_SINK_M_V_VALVE].gst)
2471 valve = wfd_sink->pipeline->mainbin[WFD_SINK_M_V_VALVE].gst;
2472 } else if (name[0] == 'a') {
2473 if (wfd_sink->pipeline->mainbin[WFD_SINK_M_A_VALVE].gst)
2474 valve = wfd_sink->pipeline->mainbin[WFD_SINK_M_A_VALVE].gst;
2477 /* add, link and run the valve */
2479 wfd_sink_debug("try to add %s to pipeline", GST_ELEMENT_NAME(valve));
2481 if (!gst_bin_add(GST_BIN(pipeline), valve)) {
2482 wfd_sink_error("failed to add %s to pipeline",
2483 GST_ELEMENT_NAME(valve));
2487 sinkpad = gst_element_get_static_pad(valve, "sink");
2489 wfd_sink_error("failed to get sink pad from %s",
2490 GST_ELEMENT_NAME(valve));
2494 wfd_sink_debug("try to link %s:%s and %s:%s",
2495 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
2496 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
2497 if (GST_PAD_LINK_OK != gst_pad_link_full(srcpad, sinkpad, GST_PAD_LINK_CHECK_NOTHING)) {
2498 wfd_sink_error("failed to link %s:%s and %s:%s",
2499 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
2500 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
2503 gst_object_unref(GST_OBJECT(srcpad));
2505 gst_object_unref(GST_OBJECT(sinkpad));
2508 wfd_sink_debug("try to sync %s's state with parent", GST_ELEMENT_NAME(valve));
2509 if (!gst_element_sync_state_with_parent(GST_ELEMENT_CAST(valve))) {
2510 wfd_sink_error("failed to sync %s state with parent",
2511 GST_PAD_NAME(valve));
2515 srcpad = gst_element_get_static_pad(valve, "src");
2517 wfd_sink_error("failed to get src pad from %s",
2518 GST_ELEMENT_NAME(valve));
2523 /* take decodebin/sinkbin */
2524 if (name[0] == 'v') {
2525 wfd_sink_debug("=========== >>>>>>>>>> Received VIDEO pad...");
2527 MMWFDSINK_PAD_PROBE(wfd_sink, pad, NULL, NULL);
2529 gst_pad_add_probe(pad,
2530 GST_PAD_PROBE_TYPE_BUFFER,
2531 _mm_wfd_sink_check_running_time,
2535 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_video_pipeline(wfd_sink, &sinkpad)) {
2536 wfd_sink_error("failed to prepare video pipeline....");
2539 } else if (name[0] == 'a') {
2540 wfd_sink_debug("=========== >>>>>>>>>> Received AUDIO pad...");
2542 MMWFDSINK_PAD_PROBE(wfd_sink, pad, NULL, NULL);
2544 gst_pad_add_probe(pad,
2545 GST_PAD_PROBE_TYPE_BUFFER,
2546 _mm_wfd_sink_check_running_time,
2550 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_audio_pipeline(wfd_sink, &sinkpad)) {
2551 wfd_sink_error("failed to prepare audio pipeline....");
2555 wfd_sink_error("unexceptable pad is added!!!");
2560 wfd_sink_debug("try to link %s:%s and %s:%s",
2561 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
2562 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
2563 if (GST_PAD_LINK_OK != gst_pad_link_full(srcpad, sinkpad, GST_PAD_LINK_CHECK_NOTHING)) {
2564 wfd_sink_error("failed to link %s:%s and %s:%s",
2565 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
2566 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
2571 MMWFDSINK_GENERATE_DOT_IF_ENABLED(wfd_sink, "video-pad-added-pipeline");
2572 else if (name[0] == 'a')
2573 MMWFDSINK_GENERATE_DOT_IF_ENABLED(wfd_sink, "audio-pad-added-pipeline");
2576 MMWFDSINK_FREEIF(name);
2579 gst_object_unref(GST_OBJECT(srcpad));
2583 gst_object_unref(GST_OBJECT(sinkpad));
2586 wfd_sink_debug_fleave();
2592 /* need to notify to app */
2593 MMWFDSINK_POST_MESSAGE(wfd_sink,
2594 MM_ERROR_WFD_INTERNAL,
2595 MMWFDSINK_CURRENT_STATE(wfd_sink));
2601 __mm_wfd_sink_change_av_format(GstElement *wfdsrc, gpointer *need_to_flush, gpointer data)
2603 mm_wfd_sink_t *wfd_sink = (mm_wfd_sink_t *)data;
2605 wfd_sink_debug_fenter();
2607 wfd_sink_return_if_fail(wfd_sink);
2608 wfd_sink_return_if_fail(need_to_flush);
2610 if (MMWFDSINK_CURRENT_STATE(wfd_sink) == MM_WFD_SINK_STATE_PLAYING) {
2611 wfd_sink_debug("need to flush pipeline");
2612 *need_to_flush = (gpointer) TRUE;
2614 wfd_sink_debug("don't need to flush pipeline");
2615 *need_to_flush = (gpointer) FALSE;
2619 wfd_sink_debug_fleave();
2624 __mm_wfd_sink_update_stream_info(GstElement *wfdsrc, GstStructure *str, gpointer data)
2626 mm_wfd_sink_t *wfd_sink = (mm_wfd_sink_t *)data;
2627 MMWFDSinkStreamInfo *stream_info = NULL;
2628 gint is_valid_audio_format = FALSE;
2629 gint is_valid_video_format = FALSE;
2630 gchar *audio_format;
2631 gchar *video_format;
2633 wfd_sink_debug_fenter();
2634 wfd_sink_return_if_fail(str && GST_IS_STRUCTURE(str));
2635 wfd_sink_return_if_fail(wfd_sink);
2637 stream_info = &wfd_sink->stream_info;
2639 if (gst_structure_has_field(str, "audio_format")) {
2640 is_valid_audio_format = TRUE;
2641 audio_format = g_strdup(gst_structure_get_string(str, "audio_format"));
2642 if (g_strrstr(audio_format, "AAC"))
2643 stream_info->audio_stream_info.codec = MM_WFD_SINK_AUDIO_CODEC_AAC;
2644 else if (g_strrstr(audio_format, "AC3"))
2645 stream_info->audio_stream_info.codec = MM_WFD_SINK_AUDIO_CODEC_AC3;
2646 else if (g_strrstr(audio_format, "LPCM"))
2647 stream_info->audio_stream_info.codec = MM_WFD_SINK_AUDIO_CODEC_LPCM;
2649 wfd_sink_error("invalid audio format(%s)...", audio_format);
2650 is_valid_audio_format = FALSE;
2652 if (is_valid_audio_format == TRUE) {
2653 if (gst_structure_has_field(str, "audio_rate"))
2654 gst_structure_get_int(str, "audio_rate", &stream_info->audio_stream_info.sample_rate);
2655 if (gst_structure_has_field(str, "audio_channels"))
2656 gst_structure_get_int(str, "audio_channels", &stream_info->audio_stream_info.channels);
2657 if (gst_structure_has_field(str, "audio_bitwidth"))
2658 gst_structure_get_int(str, "audio_bitwidth", &stream_info->audio_stream_info.bitwidth);
2660 WFD_SINK_MANAGER_APPEND_CMD(wfd_sink, WFD_SINK_MANAGER_CMD_PREPARE_A_PIPELINE);
2662 wfd_sink_debug("audio_format : %s \n \t rate : %d \n \t channels : %d \n \t bitwidth : %d \n \t \n",
2664 stream_info->audio_stream_info.sample_rate,
2665 stream_info->audio_stream_info.channels,
2666 stream_info->audio_stream_info.bitwidth);
2669 g_free(audio_format);
2671 if (gst_structure_has_field(str, "video_format")) {
2672 is_valid_video_format = TRUE;
2673 video_format = g_strdup(gst_structure_get_string(str, "video_format"));
2674 if (g_strrstr(video_format, "H264")) {
2675 stream_info->video_stream_info.codec = MM_WFD_SINK_VIDEO_CODEC_H264;
2676 } else if (g_strrstr(video_format, "H265")) {
2677 stream_info->video_stream_info.codec = MM_WFD_SINK_VIDEO_CODEC_H265;
2679 wfd_sink_error("invalid video format(%s)...", video_format);
2680 is_valid_video_format = FALSE;
2683 if (is_valid_video_format == TRUE) {
2684 if (gst_structure_has_field(str, "video_width"))
2685 gst_structure_get_int(str, "video_width", &stream_info->video_stream_info.width);
2686 if (gst_structure_has_field(str, "video_height"))
2687 gst_structure_get_int(str, "video_height", &stream_info->video_stream_info.height);
2688 if (gst_structure_has_field(str, "video_framerate"))
2689 gst_structure_get_int(str, "video_framerate", &stream_info->video_stream_info.frame_rate);
2691 WFD_SINK_MANAGER_APPEND_CMD(wfd_sink, WFD_SINK_MANAGER_CMD_PREPARE_V_PIPELINE);
2693 wfd_sink_debug("video_format : %s \n \t width : %d \n \t height : %d \n \t frame_rate : %d \n \t",
2695 stream_info->video_stream_info.width,
2696 stream_info->video_stream_info.height,
2697 stream_info->video_stream_info.frame_rate);
2700 g_free(video_format);
2703 WFD_SINK_MANAGER_SIGNAL_CMD(wfd_sink);
2705 wfd_sink_debug_fleave();
2708 static void __mm_wfd_sink_prepare_video_resolution(gint resolution, guint64 *CEA_resolution,
2709 guint64 *VESA_resolution, guint64 *HH_resolution)
2711 if (resolution == MM_WFD_SINK_RESOLUTION_UNKNOWN) return;
2713 *CEA_resolution = 0;
2714 *VESA_resolution = 0;
2717 if (resolution & MM_WFD_SINK_RESOLUTION_1920x1080_P30)
2718 *CEA_resolution |= WFD_CEA_1920x1080P30;
2720 if (resolution & MM_WFD_SINK_RESOLUTION_1280x720_P30)
2721 *CEA_resolution |= WFD_CEA_1280x720P30;
2723 if (resolution & MM_WFD_SINK_RESOLUTION_960x540_P30)
2724 *HH_resolution |= WFD_HH_960x540P30;
2726 if (resolution & MM_WFD_SINK_RESOLUTION_864x480_P30)
2727 *HH_resolution |= WFD_HH_864x480P30;
2729 if (resolution & MM_WFD_SINK_RESOLUTION_720x480_P60)
2730 *CEA_resolution |= WFD_CEA_720x480P60;
2732 if (resolution & MM_WFD_SINK_RESOLUTION_640x480_P60)
2733 *CEA_resolution |= WFD_CEA_640x480P60;
2735 if (resolution & MM_WFD_SINK_RESOLUTION_640x360_P30)
2736 *HH_resolution |= WFD_HH_640x360P30;
2739 static int __mm_wfd_sink_prepare_source(mm_wfd_sink_t *wfd_sink, GstElement *wfdsrc)
2741 GstStructure *wfd_audio_codecs = NULL;
2742 GstStructure *wfd_video_formats = NULL;
2743 GstStructure *wfd_content_protection = NULL;
2744 GstStructure *wfd2_video_formats = NULL;
2745 GstStructure *wfd2_audio_codecs = NULL;
2746 GstStructure *wfd_coupled_sink= NULL;
2747 gint hdcp_version = 0;
2749 guint64 CEA_resolution = 0;
2750 guint64 VESA_resolution = 0;
2751 guint64 HH_resolution = 0;
2752 GObjectClass *klass;
2754 wfd_sink_debug_fenter();
2756 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
2757 wfd_sink_return_val_if_fail(wfd_sink->attrs, MM_ERROR_WFD_NOT_INITIALIZED);
2758 wfd_sink_return_val_if_fail(wfdsrc, MM_ERROR_WFD_NOT_INITIALIZED);
2760 klass = G_OBJECT_GET_CLASS(G_OBJECT(wfdsrc));
2762 if (g_object_class_find_property(klass, "enable-pad-probe")) /* for common wfdsrc */
2763 g_object_set(G_OBJECT(wfdsrc), "enable-pad-probe", wfd_sink->ini.trace_buffers_of_wfdsrc, NULL);
2764 g_object_set(G_OBJECT(wfdsrc), "udp-buffer-size", 2097152, NULL);
2765 g_object_set(G_OBJECT(wfdsrc), "latency", wfd_sink->ini.jitter_buffer_latency, NULL);
2766 g_object_set(G_OBJECT(wfdsrc), "user-agent", wfd_sink->ini.user_agent, NULL);
2767 g_object_set(G_OBJECT(wfdsrc), "dump-rtsp-message", wfd_sink->ini.dump_rtsp_message, NULL);
2768 if (g_object_class_find_property(klass, "dump-rtp-data"))
2769 g_object_set(G_OBJECT(wfdsrc), "dump-rtp-data", wfd_sink->ini.dump_rtp_data, NULL);
2770 if (g_object_class_find_property(klass, "trace-first-buffer"))
2771 g_object_set(G_OBJECT(wfdsrc), "trace-first-buffer", wfd_sink->ini.trace_first_buffer, NULL);
2772 if (g_object_class_find_property(klass, "trace-buffers"))
2773 g_object_set(G_OBJECT(wfdsrc), "trace-buffers", wfd_sink->ini.trace_buffers, NULL);
2774 if (g_object_class_find_property(klass, "do-request"))
2775 g_object_set(G_OBJECT(wfdsrc), "do-request", wfd_sink->ini.enable_retransmission, NULL);
2777 /* set audio parameter for Wi-Fi Display session negotiation */
2778 wfd_audio_codecs = gst_structure_new("wfd_audio_codecs",
2779 "audio_codec", G_TYPE_UINT, wfd_sink->ini.wfd_audio_codecs.audio_codec,
2780 "audio_latency", G_TYPE_UINT, wfd_sink->ini.wfd_audio_codecs.audio_latency,
2781 "audio_channels", G_TYPE_UINT, wfd_sink->ini.wfd_audio_codecs.audio_channel,
2782 "audio_sampling_frequency", G_TYPE_UINT, wfd_sink->ini.wfd_audio_codecs.audio_sampling_frequency,
2785 if (wfd_audio_codecs) {
2786 g_object_set(G_OBJECT(wfdsrc), "wfd-audio-codecs", wfd_audio_codecs, NULL);
2787 gst_structure_free(wfd_audio_codecs);
2788 wfd_audio_codecs = NULL;
2791 /* set video parameter for Wi-Fi Display session negotiation */
2792 CEA_resolution = wfd_sink->ini.wfd_video_formats.video_cea_support;
2793 VESA_resolution = wfd_sink->ini.wfd_video_formats.video_vesa_support;
2794 HH_resolution = wfd_sink->ini.wfd_video_formats.video_hh_support;
2796 __mm_wfd_sink_prepare_video_resolution(wfd_sink->supportive_resolution,
2797 &CEA_resolution, &VESA_resolution, &HH_resolution);
2798 wfd_video_formats = gst_structure_new("wfd_video_formats",
2799 "video_codec", G_TYPE_UINT, wfd_sink->ini.wfd_video_formats.video_codec,
2800 "video_native_resolution", G_TYPE_UINT, wfd_sink->ini.wfd_video_formats.video_native_resolution,
2801 "video_cea_support", G_TYPE_UINT64, CEA_resolution,
2802 "video_vesa_support", G_TYPE_UINT64, VESA_resolution,
2803 "video_hh_support", G_TYPE_UINT64, HH_resolution,
2804 "video_profile", G_TYPE_UINT, wfd_sink->ini.wfd_video_formats.video_profile,
2805 "video_level", G_TYPE_UINT, wfd_sink->ini.wfd_video_formats.video_level,
2806 "video_latency", G_TYPE_UINT, wfd_sink->ini.wfd_video_formats.video_latency,
2807 "video_vertical_resolution", G_TYPE_INT, wfd_sink->ini.wfd_video_formats.video_vertical_resolution,
2808 "video_horizontal_resolution", G_TYPE_INT, wfd_sink->ini.wfd_video_formats.video_horizontal_resolution,
2809 "video_minimum_slicing", G_TYPE_INT, wfd_sink->ini.wfd_video_formats.video_minimum_slicing,
2810 "video_slice_enc_param", G_TYPE_INT, wfd_sink->ini.wfd_video_formats.video_slice_enc_param,
2811 "video_framerate_control_support", G_TYPE_INT, wfd_sink->ini.wfd_video_formats.video_framerate_control_support,
2814 if (wfd_video_formats) {
2815 g_object_set(G_OBJECT(wfdsrc), "wfd-video-formats", wfd_video_formats, NULL);
2816 gst_structure_free(wfd_video_formats);
2817 wfd_video_formats = NULL;
2819 /* set coupled sink information for Wi-Fi Display session negotiation */
2820 wfd_coupled_sink = gst_structure_new("wfd_coupled_sink",
2821 "coupled_sink_status", G_TYPE_INT, wfd_sink->coupled_sink_status,
2822 "coupled_sink_address", G_TYPE_STRING, wfd_sink->coupled_sink_address,
2823 "is_coupled_sink_supported", G_TYPE_BOOLEAN, wfd_sink->is_coupled_sink_supported,
2826 if (wfd_coupled_sink) {
2827 g_object_set(G_OBJECT(wfdsrc), "wfd-coupled-sink", wfd_coupled_sink, NULL);
2828 gst_structure_free(wfd_coupled_sink);
2829 wfd_coupled_sink = NULL;
2832 /* set hdcp parameter for Wi-Fi Display session negotiation */
2833 if (wfd_sink->ini.wfd_content_protection.enable_hdcp) {
2834 mm_attrs_get_int_by_name(wfd_sink->attrs, "hdcp_version", &hdcp_version);
2835 mm_attrs_get_int_by_name(wfd_sink->attrs, "hdcp_port", &hdcp_port);
2836 wfd_sink_debug("set hdcp version %d with %d port", hdcp_version, hdcp_port);
2838 wfd_content_protection = gst_structure_new("wfd_content_protection",
2839 "hdcp_version", G_TYPE_INT, hdcp_version,
2840 "hdcp_port_no", G_TYPE_INT, hdcp_port,
2843 if (wfd_content_protection) {
2844 g_object_set(G_OBJECT(wfdsrc), "wfd-content-protection", wfd_content_protection, NULL);
2845 gst_structure_free(wfd_content_protection);
2846 wfd_content_protection = NULL;
2850 if (g_object_class_find_property(klass, "wfd2-audio-codecs")) {
2851 /* set audio parameter for Wi-Fi Display R2 session negotiation */
2852 wfd2_audio_codecs = gst_structure_new("wfd2-audio-codecs",
2853 "audio_codec", G_TYPE_UINT, wfd_sink->ini.wfd2_audio_codecs.audio_codec,
2854 "audio_lpcm_mode", G_TYPE_UINT, wfd_sink->ini.wfd2_audio_codecs.audio_lpcm_mode,
2855 "audio_aac_mode", G_TYPE_UINT, wfd_sink->ini.wfd2_audio_codecs.audio_aac_mode,
2856 "audio_ac3_mode", G_TYPE_UINT, wfd_sink->ini.wfd2_audio_codecs.audio_ac3_mode,
2859 if (wfd2_audio_codecs) {
2860 g_object_set(G_OBJECT(wfdsrc), "wfd2-audio-codecs", wfd2_audio_codecs, NULL);
2861 gst_structure_free(wfd2_audio_codecs);
2862 wfd2_audio_codecs = NULL;
2866 if (g_object_class_find_property(klass, "wfd2-video-format-h264")) {
2867 /* set video parameter for Wi-Fi Display R2 session negotiation */
2868 CEA_resolution = wfd_sink->ini.wfd2_video_h264_info.video_cea_support;
2869 VESA_resolution = wfd_sink->ini.wfd2_video_h264_info.video_vesa_support;
2870 HH_resolution = wfd_sink->ini.wfd2_video_h264_info.video_hh_support;
2872 if (wfd_sink->ini.wfd2_video_formats.video_codec & WFD_VIDEO_H264) {
2873 wfd2_video_formats = gst_structure_new("wfd2-video-format-h264",
2874 "video_codec", G_TYPE_UINT, WFD_VIDEO_H264,
2875 "video_native_resolution", G_TYPE_UINT, wfd_sink->ini.wfd2_video_formats.video_native_resolution,
2876 "video_cea_support", G_TYPE_UINT64, CEA_resolution,
2877 "video_vesa_support", G_TYPE_UINT64, VESA_resolution,
2878 "video_hh_support", G_TYPE_UINT64, HH_resolution,
2879 "video_profile", G_TYPE_UINT, wfd_sink->ini.wfd2_video_h264_info.video_profile,
2880 "video_level", G_TYPE_UINT, wfd_sink->ini.wfd2_video_h264_info.video_level,
2881 "video_latency", G_TYPE_UINT, wfd_sink->ini.wfd2_video_h264_info.video_latency,
2882 "video_vertical_resolution", G_TYPE_INT, wfd_sink->ini.wfd2_video_h264_info.video_vertical_resolution,
2883 "video_horizontal_resolution", G_TYPE_INT, wfd_sink->ini.wfd2_video_h264_info.video_horizontal_resolution,
2884 "video_minimum_slicing", G_TYPE_INT, wfd_sink->ini.wfd2_video_h264_info.video_minimum_slicing,
2885 "video_slice_enc_param", G_TYPE_INT, wfd_sink->ini.wfd2_video_h264_info.video_slice_enc_param,
2886 "video_framerate_control_support", G_TYPE_INT, wfd_sink->ini.wfd2_video_h264_info.video_framerate_control_support,
2887 "video_non_transcoding_support", G_TYPE_INT, wfd_sink->ini.wfd2_video_formats.video_non_transcoding_support,
2890 if (wfd2_video_formats) {
2891 g_object_set(G_OBJECT(wfdsrc), "wfd2-video-format-h264", wfd2_video_formats, NULL);
2892 gst_structure_free(wfd2_video_formats);
2893 wfd2_video_formats = NULL;
2898 if (g_object_class_find_property(klass, "wfd2-video-format-h265")) {
2899 /* set video parameter for Wi-Fi Display R2 session negotiation */
2900 CEA_resolution = wfd_sink->ini.wfd2_video_h265_info.video_cea_support;
2901 VESA_resolution = wfd_sink->ini.wfd2_video_h265_info.video_vesa_support;
2902 HH_resolution = wfd_sink->ini.wfd2_video_h265_info.video_hh_support;
2904 if (wfd_sink->ini.wfd2_video_formats.video_codec & WFD_VIDEO_H265) {
2905 wfd2_video_formats = gst_structure_new("wfd2-video-format-h265",
2906 "video_codec", G_TYPE_UINT, WFD_VIDEO_H265,
2907 "video_native_resolution", G_TYPE_UINT, wfd_sink->ini.wfd2_video_formats.video_native_resolution,
2908 "video_cea_support", G_TYPE_UINT64, CEA_resolution,
2909 "video_vesa_support", G_TYPE_UINT64, VESA_resolution,
2910 "video_hh_support", G_TYPE_UINT64, HH_resolution,
2911 "video_profile", G_TYPE_UINT, wfd_sink->ini.wfd2_video_h265_info.video_profile,
2912 "video_level", G_TYPE_UINT, wfd_sink->ini.wfd2_video_h265_info.video_level,
2913 "video_latency", G_TYPE_UINT, wfd_sink->ini.wfd2_video_h265_info.video_latency,
2914 "video_vertical_resolution", G_TYPE_INT, wfd_sink->ini.wfd2_video_h265_info.video_vertical_resolution,
2915 "video_horizontal_resolution", G_TYPE_INT, wfd_sink->ini.wfd2_video_h265_info.video_horizontal_resolution,
2916 "video_minimum_slicing", G_TYPE_INT, wfd_sink->ini.wfd2_video_h265_info.video_minimum_slicing,
2917 "video_slice_enc_param", G_TYPE_INT, wfd_sink->ini.wfd2_video_h265_info.video_slice_enc_param,
2918 "video_framerate_control_support", G_TYPE_INT, wfd_sink->ini.wfd2_video_h265_info.video_framerate_control_support,
2919 "video_non_transcoding_support", G_TYPE_INT, wfd_sink->ini.wfd2_video_formats.video_non_transcoding_support,
2922 if (wfd2_video_formats) {
2923 g_object_set(G_OBJECT(wfdsrc), "wfd2-video-format-h265", wfd2_video_formats, NULL);
2924 gst_structure_free(wfd2_video_formats);
2925 wfd2_video_formats = NULL;
2930 wfd_sink->update_stream_info_sig_id = g_signal_connect(wfdsrc, "update-media-info",
2931 G_CALLBACK(__mm_wfd_sink_update_stream_info), wfd_sink);
2933 wfd_sink->change_av_format_sig_id = g_signal_connect(wfdsrc, "change-av-format",
2934 G_CALLBACK(__mm_wfd_sink_change_av_format), wfd_sink);
2936 wfd_sink_debug_fleave();
2938 return MM_ERROR_NONE;
2941 static int __mm_wfd_sink_prepare_demux(mm_wfd_sink_t *wfd_sink, GstElement *demux)
2943 wfd_sink_debug_fenter();
2945 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
2946 wfd_sink_return_val_if_fail(demux, MM_ERROR_WFD_NOT_INITIALIZED);
2948 g_signal_connect(demux, "pad-added",
2949 G_CALLBACK(__mm_wfd_sink_demux_pad_added), wfd_sink);
2951 wfd_sink_debug_fleave();
2953 return MM_ERROR_NONE;
2956 static void __mm_wfd_sink_queue_overrun(GstElement *queue, gpointer u_data)
2960 wfd_sink_debug_fenter();
2962 return_if_fail(queue);
2964 g_object_get(G_OBJECT(queue), "current-level-time", &time, NULL);
2966 wfd_sink_warning("%s is overrun(%" GST_TIME_FORMAT")",
2967 GST_ELEMENT_NAME(queue), GST_TIME_ARGS(time));
2969 wfd_sink_debug_fleave();
2974 static void __mm_wfd_sink_prepare_queue(mm_wfd_sink_t *wfd_sink, GstElement *queue)
2976 wfd_sink_debug_fenter();
2978 wfd_sink_return_if_fail(wfd_sink);
2979 wfd_sink_return_if_fail(queue);
2981 /* set maximum buffer size of queue as 3sec */
2982 g_object_set(G_OBJECT(queue), "max-size-bytes", 0, NULL);
2983 g_object_set(G_OBJECT(queue), "max-size-buffers", 0, NULL);
2984 g_object_set(G_OBJECT(queue), "max-size-time", (guint64)3000000000ULL, NULL);
2985 g_signal_connect(queue, "overrun",
2986 G_CALLBACK(__mm_wfd_sink_queue_overrun), wfd_sink);
2988 wfd_sink_debug_fleave();
2994 int __mm_wfd_sink_create_pipeline(mm_wfd_sink_t *wfd_sink)
2996 MMWFDSinkGstElement *mainbin = NULL;
2997 GList *element_bucket = NULL;
3001 wfd_sink_debug_fenter();
3003 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
3004 wfd_sink_return_val_if_fail(wfd_sink->attrs, MM_ERROR_WFD_NOT_INITIALIZED);
3006 /* Create pipeline */
3007 wfd_sink->pipeline = (MMWFDSinkGstPipelineInfo *) g_malloc0(sizeof(MMWFDSinkGstPipelineInfo));
3008 if (wfd_sink->pipeline == NULL)
3011 memset(wfd_sink->pipeline, 0, sizeof(MMWFDSinkGstPipelineInfo));
3013 /* create mainbin */
3014 mainbin = (MMWFDSinkGstElement *) g_malloc0(sizeof(MMWFDSinkGstElement) * WFD_SINK_M_NUM);
3015 if (mainbin == NULL)
3018 memset(mainbin, 0, sizeof(MMWFDSinkGstElement) * WFD_SINK_M_NUM);
3020 /* create pipeline */
3021 mainbin[WFD_SINK_M_PIPE].id = WFD_SINK_M_PIPE;
3022 mainbin[WFD_SINK_M_PIPE].gst = gst_pipeline_new("wfdsink");
3023 if (!mainbin[WFD_SINK_M_PIPE].gst) {
3024 wfd_sink_error("failed to create pipeline");
3029 MMWFDSINK_CREATE_ELEMENT(mainbin, WFD_SINK_M_SRC, wfd_sink->ini.name_of_source, "wfdsink_source", TRUE);
3030 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_SRC].gst, "src");
3031 if (mainbin[WFD_SINK_M_SRC].gst) {
3032 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_source(wfd_sink, mainbin[WFD_SINK_M_SRC].gst)) {
3033 wfd_sink_error("failed to prepare wfdsrc...");
3038 /* create rtpmp2tdepay */
3039 MMWFDSINK_CREATE_ELEMENT(mainbin, WFD_SINK_M_DEPAY, "rtpmp2tdepay", "wfdsink_depay", TRUE);
3040 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_DEPAY].gst, "src");
3041 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_DEPAY].gst, "sink");
3043 MMWFDSINK_TS_DATA_DUMP(wfd_sink, mainbin[WFD_SINK_M_DEPAY].gst, "src");
3045 /* create queue for ts */
3046 MMWFDSINK_CREATE_ELEMENT(mainbin, WFD_SINK_M_QUEUE, "queue", "ts_queue", TRUE);
3047 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_QUEUE].gst, "src");
3048 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_QUEUE].gst, "sink");
3049 g_object_set(G_OBJECT(mainbin[WFD_SINK_M_QUEUE].gst), "max-size-buffers", 200000, NULL);
3051 /* create valve for demux */
3052 MMWFDSINK_CREATE_ELEMENT(mainbin, WFD_SINK_M_D_VALVE, "valve", "demux_valve", TRUE);
3053 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_D_VALVE].gst, "src");
3054 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_D_VALVE].gst, "sink");
3056 /* create tsdemuxer*/
3057 MMWFDSINK_CREATE_ELEMENT(mainbin, WFD_SINK_M_DEMUX, wfd_sink->ini.name_of_tsdemux, "wfdsink_demux", TRUE);
3058 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_DEMUX].gst, "sink");
3059 if (mainbin[WFD_SINK_M_DEMUX].gst) {
3060 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_demux(wfd_sink, mainbin[WFD_SINK_M_DEMUX].gst)) {
3061 wfd_sink_error("failed to prepare demux...");
3066 /* create valve for audio */
3067 MMWFDSINK_CREATE_ELEMENT(mainbin, WFD_SINK_M_A_VALVE, "valve", "audio_valve", FALSE);
3068 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_A_VALVE].gst, "src");
3069 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_A_VALVE].gst, "sink");
3071 /* create valve for video */
3072 MMWFDSINK_CREATE_ELEMENT(mainbin, WFD_SINK_M_V_VALVE, "valve", "video_valve", FALSE);
3073 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_V_VALVE].gst, "src");
3074 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_V_VALVE].gst, "sink");
3076 /* adding created elements to pipeline */
3077 if (!__mm_wfd_sink_gst_element_add_bucket_to_bin(GST_BIN_CAST(mainbin[WFD_SINK_M_PIPE].gst), element_bucket, FALSE)) {
3078 wfd_sink_error("failed to add elements");
3082 /* linking elements in the bucket by added order. */
3083 if (__mm_wfd_sink_gst_element_link_bucket(element_bucket) == -1) {
3084 wfd_sink_error("failed to link elements");
3088 /* connect bus callback */
3089 bus = gst_pipeline_get_bus(GST_PIPELINE(mainbin[WFD_SINK_M_PIPE].gst));
3091 wfd_sink_error("cannot get bus from pipeline.");
3095 /* add bus message callback*/
3096 wfd_sink->msg_callback_id = gst_bus_add_watch(bus, (GstBusFunc)_mm_wfd_sink_msg_callback, wfd_sink);
3098 /* set sync handler to get tag synchronously */
3099 gst_bus_set_sync_handler(bus, _mm_wfd_bus_sync_callback, wfd_sink, NULL);
3101 g_list_free(element_bucket);
3102 element_bucket = NULL;
3103 gst_object_unref(GST_OBJECT(bus));
3106 /* now we have completed mainbin. take it */
3107 wfd_sink->pipeline->mainbin = mainbin;
3109 wfd_sink_debug_fleave();
3111 return MM_ERROR_NONE;
3115 wfd_sink_error("ERROR : releasing pipeline");
3117 if (element_bucket) {
3118 g_list_free(element_bucket);
3119 element_bucket = NULL;
3122 /* release element which are not added to bin */
3123 for (i = 1; i < WFD_SINK_M_NUM; i++) { /* NOTE : skip pipeline */
3124 if (mainbin != NULL && mainbin[i].gst) {
3125 GstObject *parent = NULL;
3126 parent = gst_element_get_parent(mainbin[i].gst);
3129 gst_object_unref(GST_OBJECT(mainbin[i].gst));
3130 mainbin[i].gst = NULL;
3132 gst_object_unref(GST_OBJECT(parent));
3138 /* release mainbin with it's childs */
3139 if (mainbin != NULL && mainbin[WFD_SINK_M_PIPE].gst) {
3140 gst_object_unref(GST_OBJECT(mainbin[WFD_SINK_M_PIPE].gst));
3141 mainbin[WFD_SINK_M_PIPE].gst = NULL;
3144 MMWFDSINK_FREEIF(mainbin);
3146 MMWFDSINK_FREEIF(wfd_sink->pipeline);
3148 return MM_ERROR_WFD_INTERNAL;
3151 int __mm_wfd_sink_link_audio_decodebin(mm_wfd_sink_t *wfd_sink)
3153 MMWFDSinkGstElement *a_decodebin = NULL;
3154 MMWFDSinkGstElement *first_element = NULL;
3155 MMWFDSinkGstElement *last_element = NULL;
3156 GList *element_bucket = NULL;
3157 GstPad *sinkpad = NULL;
3158 GstPad *srcpad = NULL;
3159 GstPad *ghostpad = NULL;
3160 GList *list_temp = NULL;
3162 wfd_sink_debug_fenter();
3164 wfd_sink_return_val_if_fail(wfd_sink &&
3165 wfd_sink->pipeline &&
3166 wfd_sink->pipeline->a_decodebin &&
3167 wfd_sink->pipeline->a_decodebin[WFD_SINK_A_D_BIN].gst,
3168 MM_ERROR_WFD_NOT_INITIALIZED);
3170 if (wfd_sink->audio_decodebin_is_linked) {
3171 wfd_sink_debug("audio decodebin is already linked... nothing to do");
3172 return MM_ERROR_NONE;
3175 if (wfd_sink->stream_info.audio_stream_info.codec == MM_WFD_SINK_AUDIO_CODEC_NONE) {
3176 wfd_sink_debug("Skip link audio decodebin for none audio codec.");
3177 wfd_sink_debug_fleave();
3178 return MM_ERROR_NONE;
3181 /* take audio decodebin */
3182 a_decodebin = wfd_sink->pipeline->a_decodebin;
3184 /* check audio queue */
3185 if (a_decodebin[WFD_SINK_A_D_QUEUE].gst)
3186 element_bucket = g_list_append(element_bucket, &a_decodebin[WFD_SINK_A_D_QUEUE]);
3188 /* check audio hdcp */
3189 if (a_decodebin[WFD_SINK_A_D_HDCP].gst)
3190 element_bucket = g_list_append(element_bucket, &a_decodebin[WFD_SINK_A_D_HDCP]);
3192 /* check audio codec */
3193 switch (wfd_sink->stream_info.audio_stream_info.codec) {
3194 case MM_WFD_SINK_AUDIO_CODEC_LPCM:
3195 if (a_decodebin[WFD_SINK_A_D_LPCM_CONVERTER].gst)
3196 element_bucket = g_list_append(element_bucket, &a_decodebin[WFD_SINK_A_D_LPCM_CONVERTER]);
3197 if (a_decodebin[WFD_SINK_A_D_LPCM_FILTER].gst) {
3198 GstCaps *caps = NULL;
3199 element_bucket = g_list_append(element_bucket, &a_decodebin[WFD_SINK_A_D_LPCM_FILTER]);
3200 caps = gst_caps_new_simple("audio/x-raw",
3201 "rate", G_TYPE_INT, wfd_sink->stream_info.audio_stream_info.sample_rate,
3202 "channels", G_TYPE_INT, wfd_sink->stream_info.audio_stream_info.channels,
3203 "format", G_TYPE_STRING, "S16BE", NULL);
3205 g_object_set(G_OBJECT(a_decodebin[WFD_SINK_A_D_LPCM_CONVERTER].gst), "caps", caps, NULL);
3206 gst_object_unref(GST_OBJECT(caps));
3211 case MM_WFD_SINK_AUDIO_CODEC_AAC:
3212 if (a_decodebin[WFD_SINK_A_D_AAC_PARSE].gst)
3213 element_bucket = g_list_append(element_bucket, &a_decodebin[WFD_SINK_A_D_AAC_PARSE]);
3214 if (a_decodebin[WFD_SINK_A_D_AAC_DEC].gst)
3215 element_bucket = g_list_append(element_bucket, &a_decodebin[WFD_SINK_A_D_AAC_DEC]);
3218 case MM_WFD_SINK_AUDIO_CODEC_AC3:
3219 if (a_decodebin[WFD_SINK_A_D_AC3_PARSE].gst)
3220 element_bucket = g_list_append(element_bucket, &a_decodebin[WFD_SINK_A_D_AC3_PARSE]);
3221 if (a_decodebin[WFD_SINK_A_D_AC3_DEC].gst)
3222 element_bucket = g_list_append(element_bucket, &a_decodebin[WFD_SINK_A_D_AC3_DEC]);
3226 wfd_sink_error("audio codec is not decied yet. cannot link audio decodebin...");
3231 if (element_bucket == NULL) {
3232 wfd_sink_error("there are no elements to be linked in the audio decodebin, destroy it");
3233 if (MM_ERROR_NONE != __mm_wfd_sink_destroy_audio_decodebin(wfd_sink)) {
3234 wfd_sink_error("failed to destroy audio decodebin");
3240 /* adding elements to audio decodebin */
3241 if (!__mm_wfd_sink_gst_element_add_bucket_to_bin(GST_BIN_CAST(a_decodebin[WFD_SINK_A_D_BIN].gst), element_bucket, FALSE)) {
3242 wfd_sink_error("failed to add elements to audio decodebin");
3246 /* linking elements in the bucket by added order. */
3247 if (__mm_wfd_sink_gst_element_link_bucket(element_bucket) == -1) {
3248 wfd_sink_error("failed to link elements of the audio decodebin");
3252 /* get first element's sinkpad for creating ghostpad */
3253 list_temp = g_list_first(element_bucket);
3254 if (list_temp == NULL) {
3255 wfd_sink_error("failed to get first list of the element_bucket");
3259 first_element = (MMWFDSinkGstElement *)list_temp->data;
3260 if (!first_element) {
3261 wfd_sink_error("failed to get first element of the audio decodebin");
3265 sinkpad = gst_element_get_static_pad(GST_ELEMENT(first_element->gst), "sink");
3267 wfd_sink_error("failed to get sink pad from element(%s)",
3268 GST_STR_NULL(GST_ELEMENT_NAME(first_element->gst)));
3272 ghostpad = gst_ghost_pad_new("sink", sinkpad);
3274 wfd_sink_error("failed to create ghostpad of audio decodebin");
3278 if (FALSE == gst_element_add_pad(a_decodebin[WFD_SINK_A_D_BIN].gst, ghostpad)) {
3279 wfd_sink_error("failed to add ghostpad to audio decodebin");
3282 gst_object_unref(GST_OBJECT(sinkpad));
3286 /* get last element's src for creating ghostpad */
3287 list_temp = g_list_last(element_bucket);
3288 if (list_temp == NULL) {
3289 wfd_sink_error("failed to get last list of the element_bucket");
3293 last_element = (MMWFDSinkGstElement *)list_temp->data;
3294 if (!last_element) {
3295 wfd_sink_error("failed to get last element of the audio decodebin");
3299 srcpad = gst_element_get_static_pad(GST_ELEMENT(last_element->gst), "src");
3301 wfd_sink_error("failed to get src pad from element(%s)",
3302 GST_STR_NULL(GST_ELEMENT_NAME(last_element->gst)));
3306 ghostpad = gst_ghost_pad_new("src", srcpad);
3308 wfd_sink_error("failed to create ghostpad of audio decodebin");
3312 if (FALSE == gst_element_add_pad(a_decodebin[WFD_SINK_A_D_BIN].gst, ghostpad)) {
3313 wfd_sink_error("failed to add ghostpad to audio decodebin");
3316 gst_object_unref(GST_OBJECT(srcpad));
3319 g_list_free(element_bucket);
3322 wfd_sink->audio_decodebin_is_linked = TRUE;
3324 wfd_sink_debug_fleave();
3326 return MM_ERROR_NONE;
3331 gst_object_unref(GST_OBJECT(srcpad));
3336 gst_object_unref(GST_OBJECT(sinkpad));
3340 g_list_free(element_bucket);
3342 return MM_ERROR_WFD_INTERNAL;
3345 static int __mm_wfd_sink_prepare_audiosink(mm_wfd_sink_t *wfd_sink, GstElement *audio_sink)
3347 wfd_sink_debug_fenter();
3349 /* check audiosink is created */
3350 wfd_sink_return_val_if_fail(audio_sink, MM_ERROR_WFD_INVALID_ARGUMENT);
3351 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
3353 g_object_set(G_OBJECT(audio_sink), "provide-clock", FALSE, NULL);
3354 g_object_set(G_OBJECT(audio_sink), "buffer-time", 100000LL, NULL);
3355 g_object_set(G_OBJECT(audio_sink), "slave-method", 2, NULL);
3356 g_object_set(G_OBJECT(audio_sink), "async", wfd_sink->ini.audio_sink_async, NULL);
3357 g_object_set(G_OBJECT(audio_sink), "ts-offset", (gint64)wfd_sink->ini.sink_ts_offset, NULL);
3359 wfd_sink_debug_fleave();
3361 return MM_ERROR_NONE;
3364 int __mm_wfd_sink_destroy_audio_decodebin(mm_wfd_sink_t *wfd_sink)
3366 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
3367 MMWFDSinkGstElement *a_decodebin = NULL;
3368 GstObject *parent = NULL;
3371 wfd_sink_error_fenter();
3373 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
3375 if (wfd_sink->pipeline &&
3376 wfd_sink->pipeline->a_decodebin &&
3377 wfd_sink->pipeline->a_decodebin[WFD_SINK_A_D_BIN].gst) {
3378 a_decodebin = wfd_sink->pipeline->a_decodebin;
3380 wfd_sink_debug("audio decodebin is not created, nothing to destroy");
3381 return MM_ERROR_NONE;
3384 parent = gst_element_get_parent(a_decodebin[WFD_SINK_A_D_BIN].gst);
3386 wfd_sink_debug("audio decodebin has no parent.. need to relase by itself");
3388 if (GST_STATE(a_decodebin[WFD_SINK_A_D_BIN].gst) >= GST_STATE_READY) {
3389 wfd_sink_error("try to change state of audio decodebin to NULL");
3390 ret = gst_element_set_state(a_decodebin[WFD_SINK_A_D_BIN].gst, GST_STATE_NULL);
3391 if (ret != GST_STATE_CHANGE_SUCCESS) {
3392 wfd_sink_error("failed to change state of audio decodebin to NULL");
3393 return MM_ERROR_WFD_INTERNAL;
3397 /* release element which are not added to bin */
3398 for (i = 1; i < WFD_SINK_A_D_NUM; i++) { /* NOTE : skip bin */
3399 if (a_decodebin[i].gst) {
3400 parent = gst_element_get_parent(a_decodebin[i].gst);
3402 wfd_sink_debug("unref %s(current ref %d)",
3403 GST_STR_NULL(GST_ELEMENT_NAME(a_decodebin[i].gst)),
3404 ((GObject *) a_decodebin[i].gst)->ref_count);
3405 gst_object_unref(GST_OBJECT(a_decodebin[i].gst));
3406 a_decodebin[i].gst = NULL;
3408 wfd_sink_debug("%s has parent.(current ref %d)",
3409 GST_STR_NULL(GST_ELEMENT_NAME(a_decodebin[i].gst)),
3410 ((GObject *) a_decodebin[i].gst)->ref_count);
3411 gst_object_unref(GST_OBJECT(parent));
3417 /* release audio decodebin with it's childs */
3418 if (a_decodebin[WFD_SINK_A_D_BIN].gst) {
3419 gst_object_unref(GST_OBJECT(a_decodebin[WFD_SINK_A_D_BIN].gst));
3420 a_decodebin[WFD_SINK_A_D_BIN].gst = NULL;
3424 wfd_sink_debug("audio decodebin has parent(%s), unref it ",
3425 GST_STR_NULL(GST_OBJECT_NAME(GST_OBJECT(parent))));
3427 gst_object_unref(GST_OBJECT(parent));
3431 wfd_sink->audio_decodebin_is_linked = FALSE;
3433 MMWFDSINK_FREEIF(wfd_sink->pipeline->a_decodebin);
3435 wfd_sink_error_fleave();
3437 return MM_ERROR_NONE;
3440 int __mm_wfd_sink_create_audio_decodebin(mm_wfd_sink_t *wfd_sink)
3442 MMWFDSinkGstElement *a_decodebin = NULL;
3443 gint audio_codec = WFD_AUDIO_UNKNOWN;
3444 GList *element_bucket = NULL;
3445 gboolean link = TRUE;
3448 wfd_sink_debug_fenter();
3450 wfd_sink_return_val_if_fail(wfd_sink &&
3452 MM_ERROR_WFD_NOT_INITIALIZED);
3454 if (wfd_sink->pipeline->a_decodebin != NULL) {
3455 wfd_sink_error("The audio decode bin is already created.");
3456 return MM_ERROR_NONE;
3459 if (wfd_sink->stream_info.audio_stream_info.codec == MM_WFD_SINK_AUDIO_CODEC_NONE) {
3460 wfd_sink_debug("Skip create audio decodebin for none audio codec.");
3461 wfd_sink_debug_fleave();
3462 return MM_ERROR_NONE;
3465 /* check audio decodebin could be linked now */
3466 switch (wfd_sink->stream_info.audio_stream_info.codec) {
3467 case MM_WFD_SINK_AUDIO_CODEC_AAC:
3468 audio_codec = WFD_AUDIO_AAC;
3471 case MM_WFD_SINK_AUDIO_CODEC_AC3:
3472 audio_codec = WFD_AUDIO_AC3;
3475 case MM_WFD_SINK_AUDIO_CODEC_LPCM:
3476 audio_codec = WFD_AUDIO_LPCM;
3479 case MM_WFD_SINK_AUDIO_CODEC_NONE:
3481 wfd_sink_debug("audio decodebin could NOT be linked now, just create");
3482 audio_codec = wfd_sink->ini.wfd_audio_codecs.audio_codec;
3488 a_decodebin = (MMWFDSinkGstElement *)g_malloc0(sizeof(MMWFDSinkGstElement) * WFD_SINK_A_D_NUM);
3490 wfd_sink_error("failed to allocate memory for audio decodebin");
3491 return MM_ERROR_WFD_NO_FREE_SPACE;
3494 memset(a_decodebin, 0, sizeof(MMWFDSinkGstElement) * WFD_SINK_A_D_NUM);
3496 /* create audio decodebin */
3497 a_decodebin[WFD_SINK_A_D_BIN].id = WFD_SINK_A_D_BIN;
3498 a_decodebin[WFD_SINK_A_D_BIN].gst = gst_bin_new("audio_deocebin");
3499 if (!a_decodebin[WFD_SINK_A_D_BIN].gst) {
3500 wfd_sink_error("failed to create audio decodebin");
3505 MMWFDSINK_CREATE_ELEMENT(a_decodebin, WFD_SINK_A_D_QUEUE, "queue", "audio_queue", FALSE);
3506 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_QUEUE].gst, "sink");
3507 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_QUEUE].gst, "src");
3508 if (a_decodebin[WFD_SINK_A_D_QUEUE].gst)
3509 __mm_wfd_sink_prepare_queue(wfd_sink, a_decodebin[WFD_SINK_A_D_QUEUE].gst);
3512 MMWFDSINK_CREATE_ELEMENT(a_decodebin, WFD_SINK_A_D_HDCP, wfd_sink->ini.name_of_audio_hdcp, "audio_hdcp", FALSE);
3513 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_HDCP].gst, "sink");
3514 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_HDCP].gst, "src");
3517 audio_codec = wfd_sink->ini.wfd_audio_codecs.audio_codec;
3518 if (audio_codec & WFD_AUDIO_LPCM) {
3519 /* create LPCM converter */
3520 MMWFDSINK_CREATE_ELEMENT(a_decodebin, WFD_SINK_A_D_LPCM_CONVERTER, wfd_sink->ini.name_of_lpcm_converter, "audio_lpcm_convert", FALSE);
3521 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_LPCM_CONVERTER].gst, "sink");
3522 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_LPCM_CONVERTER].gst, "src");
3524 /* create LPCM filter */
3525 MMWFDSINK_CREATE_ELEMENT(a_decodebin, WFD_SINK_A_D_LPCM_FILTER, wfd_sink->ini.name_of_lpcm_filter, "audio_lpcm_filter", FALSE);
3526 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_LPCM_FILTER].gst, "sink");
3527 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_LPCM_FILTER].gst, "src");
3530 if (audio_codec & WFD_AUDIO_AAC) {
3531 /* create AAC parse */
3532 MMWFDSINK_CREATE_ELEMENT(a_decodebin, WFD_SINK_A_D_AAC_PARSE, wfd_sink->ini.name_of_aac_parser, "audio_aac_parser", FALSE);
3533 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_AAC_PARSE].gst, "sink");
3534 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_AAC_PARSE].gst, "src");
3536 /* create AAC decoder */
3537 MMWFDSINK_CREATE_ELEMENT(a_decodebin, WFD_SINK_A_D_AAC_DEC, wfd_sink->ini.name_of_aac_decoder, "audio_aac_dec", FALSE);
3538 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_AAC_DEC].gst, "sink");
3539 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_AAC_DEC].gst, "src");
3542 if (audio_codec & WFD_AUDIO_AC3) {
3543 /* create AC3 parser */
3544 MMWFDSINK_CREATE_ELEMENT(a_decodebin, WFD_SINK_A_D_AC3_PARSE, wfd_sink->ini.name_of_ac3_parser, "audio_ac3_parser", FALSE);
3545 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_AC3_PARSE].gst, "sink");
3546 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_AC3_PARSE].gst, "src");
3548 /* create AC3 decoder */
3549 MMWFDSINK_CREATE_ELEMENT(a_decodebin, WFD_SINK_A_D_AC3_DEC, wfd_sink->ini.name_of_ac3_decoder, "audio_ac3_dec", FALSE);
3550 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_AC3_DEC].gst, "sink");
3551 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_AC3_DEC].gst, "src");
3554 g_list_free(element_bucket);
3557 wfd_sink->pipeline->a_decodebin = a_decodebin;
3559 /* link audio decodebin if audio codec is fixed */
3561 if (MM_ERROR_NONE != __mm_wfd_sink_link_audio_decodebin(wfd_sink)) {
3562 wfd_sink_error("failed to link audio decodebin, destroy audio decodebin");
3563 __mm_wfd_sink_destroy_audio_decodebin(wfd_sink);
3564 return MM_ERROR_WFD_INTERNAL;
3568 wfd_sink_debug_fleave();
3570 return MM_ERROR_NONE;
3573 wfd_sink_error("failed to create audio decodebin, release all");
3575 g_list_free(element_bucket);
3577 /* release element which are not added to bin */
3578 for (i = 1; i < WFD_SINK_A_D_NUM; i++) { /* NOTE : skip bin */
3579 if (a_decodebin != NULL && a_decodebin[i].gst) {
3580 GstObject *parent = NULL;
3581 parent = gst_element_get_parent(a_decodebin[i].gst);
3584 gst_object_unref(GST_OBJECT(a_decodebin[i].gst));
3585 a_decodebin[i].gst = NULL;
3587 gst_object_unref(GST_OBJECT(parent));
3593 /* release audio decodebin with it's childs */
3594 if (a_decodebin != NULL && a_decodebin[WFD_SINK_A_D_BIN].gst) {
3595 gst_object_unref(GST_OBJECT(a_decodebin[WFD_SINK_A_D_BIN].gst));
3596 a_decodebin[WFD_SINK_A_D_BIN].gst = NULL;
3599 MMWFDSINK_FREEIF(a_decodebin);
3601 return MM_ERROR_WFD_INTERNAL;
3604 int __mm_wfd_sink_destroy_audio_sinkbin(mm_wfd_sink_t *wfd_sink)
3606 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
3607 MMWFDSinkGstElement *a_sinkbin = NULL;
3608 GstObject *parent = NULL;
3611 wfd_sink_error_fenter();
3613 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
3615 if (wfd_sink->pipeline &&
3616 wfd_sink->pipeline->a_sinkbin &&
3617 wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_BIN].gst) {
3618 a_sinkbin = wfd_sink->pipeline->a_sinkbin;
3620 wfd_sink_debug("audio sinkbin is not created, nothing to destroy");
3621 return MM_ERROR_NONE;
3624 parent = gst_element_get_parent(a_sinkbin[WFD_SINK_A_S_BIN].gst);
3626 wfd_sink_error("audio sinkbin has no parent.. need to relase by itself");
3628 if (GST_STATE(a_sinkbin[WFD_SINK_A_S_BIN].gst) >= GST_STATE_READY) {
3629 wfd_sink_error("try to change state of audio sinkbin to NULL");
3630 ret = gst_element_set_state(a_sinkbin[WFD_SINK_A_S_BIN].gst, GST_STATE_NULL);
3631 if (ret != GST_STATE_CHANGE_SUCCESS) {
3632 wfd_sink_error("failed to change state of audio sinkbin to NULL");
3633 return MM_ERROR_WFD_INTERNAL;
3637 /* release element which are not added to bin */
3638 for (i = 1; i < WFD_SINK_A_S_NUM; i++) { /* NOTE : skip bin */
3639 if (a_sinkbin[i].gst) {
3640 parent = gst_element_get_parent(a_sinkbin[i].gst);
3642 wfd_sink_debug("unref %s(current ref %d)",
3643 GST_STR_NULL(GST_ELEMENT_NAME(a_sinkbin[i].gst)),
3644 ((GObject *) a_sinkbin[i].gst)->ref_count);
3645 gst_object_unref(GST_OBJECT(a_sinkbin[i].gst));
3646 a_sinkbin[i].gst = NULL;
3648 wfd_sink_debug("%s has parent.(current ref %d)",
3649 GST_STR_NULL(GST_ELEMENT_NAME(a_sinkbin[i].gst)),
3650 ((GObject *) a_sinkbin[i].gst)->ref_count);
3651 gst_object_unref(GST_OBJECT(parent));
3657 /* release audio sinkbin with it's childs */
3658 if (a_sinkbin[WFD_SINK_A_S_BIN].gst) {
3659 gst_object_unref(GST_OBJECT(a_sinkbin[WFD_SINK_A_S_BIN].gst));
3660 a_sinkbin[WFD_SINK_A_S_BIN].gst = NULL;
3663 wfd_sink_debug("audio sinkbin has parent(%s), unref it ",
3664 GST_STR_NULL(GST_OBJECT_NAME(GST_OBJECT(parent))));
3666 gst_object_unref(GST_OBJECT(parent));
3670 MMWFDSINK_FREEIF(wfd_sink->pipeline->a_sinkbin);
3672 wfd_sink_error_fleave();
3674 return MM_ERROR_NONE;
3677 int __mm_wfd_sink_create_audio_sinkbin(mm_wfd_sink_t *wfd_sink)
3679 MMWFDSinkGstElement *a_sinkbin = NULL;
3680 MMWFDSinkGstElement *first_element = NULL;
3681 GList *element_bucket = NULL;
3682 GstPad *ghostpad = NULL;
3685 GList *list_temp = NULL;
3687 wfd_sink_debug_fenter();
3689 wfd_sink_return_val_if_fail(wfd_sink &&
3691 MM_ERROR_WFD_NOT_INITIALIZED);
3693 if (wfd_sink->pipeline->a_sinkbin != NULL) {
3694 wfd_sink_error("The audio sink bin is already created.");
3695 return MM_ERROR_NONE;
3698 if (wfd_sink->stream_info.audio_stream_info.codec == MM_WFD_SINK_AUDIO_CODEC_NONE) {
3699 wfd_sink_error("Skip create audio sink bin for non audio codec.");
3700 wfd_sink_debug_fleave();
3701 return MM_ERROR_NONE;
3705 a_sinkbin = (MMWFDSinkGstElement *)g_malloc0(sizeof(MMWFDSinkGstElement) * WFD_SINK_A_S_NUM);
3707 wfd_sink_error("failed to allocate memory for audio sinkbin");
3708 return MM_ERROR_WFD_NO_FREE_SPACE;
3711 memset(a_sinkbin, 0, sizeof(MMWFDSinkGstElement) * WFD_SINK_A_S_NUM);
3713 /* create audio sinkbin */
3714 a_sinkbin[WFD_SINK_A_S_BIN].id = WFD_SINK_A_S_BIN;
3715 a_sinkbin[WFD_SINK_A_S_BIN].gst = gst_bin_new("audio_sinkbin");
3716 if (!a_sinkbin[WFD_SINK_A_S_BIN].gst) {
3717 wfd_sink_error("failed to create audio sinkbin");
3721 /* create resampler */
3722 MMWFDSINK_CREATE_ELEMENT(a_sinkbin, WFD_SINK_A_S_RESAMPLER,
3723 wfd_sink->ini.name_of_audio_resampler, "audio_resampler", TRUE);
3724 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_sinkbin[WFD_SINK_A_S_RESAMPLER].gst, "sink");
3725 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_sinkbin[WFD_SINK_A_S_RESAMPLER].gst, "src");
3728 MMWFDSINK_CREATE_ELEMENT(a_sinkbin, WFD_SINK_A_S_VOLUME,
3729 wfd_sink->ini.name_of_audio_volume, "audio_volume", TRUE);
3730 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_sinkbin[WFD_SINK_A_S_VOLUME].gst, "sink");
3731 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_sinkbin[WFD_SINK_A_S_VOLUME].gst, "src");
3733 MMWFDSINK_CREATE_ELEMENT(a_sinkbin, WFD_SINK_A_S_QUEUE,
3734 wfd_sink->ini.name_of_audio_sinkbin_queue, "audio_sinkbin_queue", TRUE);
3735 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_sinkbin[WFD_SINK_A_S_QUEUE].gst, "sink");
3736 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_sinkbin[WFD_SINK_A_S_QUEUE].gst, "src");
3739 MMWFDSINK_CREATE_ELEMENT(a_sinkbin, WFD_SINK_A_S_SINK,
3740 wfd_sink->ini.name_of_audio_sink, "audio_sink", TRUE);
3741 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_sinkbin[WFD_SINK_A_S_SINK].gst, "sink");
3742 if (a_sinkbin[WFD_SINK_A_S_SINK].gst) {
3743 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_audiosink(wfd_sink, a_sinkbin[WFD_SINK_A_S_SINK].gst)) {
3744 wfd_sink_error("failed to set audio sink property....");
3749 /* adding created elements to audio sinkbin */
3750 if (!__mm_wfd_sink_gst_element_add_bucket_to_bin(GST_BIN_CAST(a_sinkbin[WFD_SINK_A_S_BIN].gst), element_bucket, FALSE)) {
3751 wfd_sink_error("failed to add elements to audio sinkbin");
3755 /* linking elements in the bucket by added order. */
3756 if (__mm_wfd_sink_gst_element_link_bucket(element_bucket) == -1) {
3757 wfd_sink_error("failed to link elements fo the audio sinkbin");
3761 /* get first element's of the audio sinkbin */
3762 list_temp = g_list_first(element_bucket);
3763 if (list_temp == NULL) {
3764 wfd_sink_error("failed to get first list of the element_bucket");
3768 first_element = (MMWFDSinkGstElement *)list_temp->data;
3769 if (!first_element) {
3770 wfd_sink_error("failed to get first element of the audio sinkbin");
3774 /* get first element's sinkpad for creating ghostpad */
3775 pad = gst_element_get_static_pad(GST_ELEMENT(first_element->gst), "sink");
3777 wfd_sink_error("failed to get sink pad from element(%s)",
3778 GST_STR_NULL(GST_ELEMENT_NAME(first_element->gst)));
3782 ghostpad = gst_ghost_pad_new("sink", pad);
3784 wfd_sink_error("failed to create ghostpad of audio sinkbin");
3788 if (FALSE == gst_element_add_pad(a_sinkbin[WFD_SINK_A_S_BIN].gst, ghostpad)) {
3789 wfd_sink_error("failed to add ghostpad to audio sinkbin");
3792 gst_object_unref(GST_OBJECT(pad));
3795 g_list_free(element_bucket);
3796 element_bucket = NULL;
3799 wfd_sink->pipeline->a_sinkbin = a_sinkbin;
3801 wfd_sink_debug_fleave();
3803 return MM_ERROR_NONE;
3806 wfd_sink_error("failed to create audio sinkbin, releasing all");
3809 gst_object_unref(GST_OBJECT(pad));
3813 gst_object_unref(GST_OBJECT(ghostpad));
3816 if (element_bucket) {
3817 g_list_free(element_bucket);
3818 element_bucket = NULL;
3821 /* release element which are not added to bin */
3822 for (i = 1; i < WFD_SINK_A_S_NUM; i++) { /* NOTE : skip bin */
3823 if (a_sinkbin != NULL && a_sinkbin[i].gst) {
3824 GstObject *parent = NULL;
3825 parent = gst_element_get_parent(a_sinkbin[i].gst);
3828 gst_object_unref(GST_OBJECT(a_sinkbin[i].gst));
3829 a_sinkbin[i].gst = NULL;
3831 gst_object_unref(GST_OBJECT(parent));
3837 /* release audio sinkbin with it's childs */
3838 if (a_sinkbin != NULL && a_sinkbin[WFD_SINK_A_S_BIN].gst) {
3839 gst_object_unref(GST_OBJECT(a_sinkbin[WFD_SINK_A_S_BIN].gst));
3840 a_sinkbin[WFD_SINK_A_S_BIN].gst = NULL;
3842 MMWFDSINK_FREEIF(a_sinkbin);
3844 return MM_ERROR_WFD_INTERNAL;
3847 int __mm_wfd_sink_link_video_decodebin(mm_wfd_sink_t *wfd_sink)
3849 MMWFDSinkGstElement *v_decodebin = NULL;
3850 MMWFDSinkGstElement *first_element = NULL;
3851 MMWFDSinkGstElement *last_element = NULL;
3852 GList *element_bucket = NULL;
3853 GList *list_temp = NULL;
3854 GstPad *sinkpad = NULL;
3855 GstPad *srcpad = NULL;
3856 GstPad *ghostpad = NULL;
3858 wfd_sink_debug_fenter();
3860 wfd_sink_return_val_if_fail(wfd_sink &&
3861 wfd_sink->pipeline &&
3862 wfd_sink->pipeline->v_decodebin &&
3863 wfd_sink->pipeline->v_decodebin[WFD_SINK_V_D_BIN].gst,
3864 MM_ERROR_WFD_NOT_INITIALIZED);
3866 if (wfd_sink->video_decodebin_is_linked) {
3867 wfd_sink_debug("video decodebin is already linked... nothing to do");
3868 return MM_ERROR_NONE;
3871 if (wfd_sink->stream_info.video_stream_info.codec == MM_WFD_SINK_VIDEO_CODEC_NONE) {
3872 wfd_sink_debug("Skip link video decodebin for none video codec.");
3873 wfd_sink_debug_fleave();
3874 return MM_ERROR_NONE;
3877 /* take video decodebin */
3878 v_decodebin = wfd_sink->pipeline->v_decodebin;
3880 /* check video queue */
3881 if (v_decodebin[WFD_SINK_V_D_QUEUE].gst)
3882 element_bucket = g_list_append(element_bucket, &v_decodebin[WFD_SINK_V_D_QUEUE]);
3884 /* check video hdcp */
3885 if (v_decodebin[WFD_SINK_V_D_HDCP].gst)
3886 element_bucket = g_list_append(element_bucket, &v_decodebin[WFD_SINK_V_D_HDCP]);
3888 /* check video codec */
3889 switch (wfd_sink->stream_info.video_stream_info.codec) {
3890 case MM_WFD_SINK_VIDEO_CODEC_H264:
3891 if (v_decodebin[WFD_SINK_V_D_H264_PARSE].gst)
3892 element_bucket = g_list_append(element_bucket, &v_decodebin[WFD_SINK_V_D_H264_PARSE]);
3893 if (v_decodebin[WFD_SINK_V_D_H264_DEC].gst)
3894 element_bucket = g_list_append(element_bucket, &v_decodebin[WFD_SINK_V_D_H264_DEC]);
3897 case MM_WFD_SINK_VIDEO_CODEC_H265:
3898 if (v_decodebin[WFD_SINK_V_D_H265_PARSE].gst)
3899 element_bucket = g_list_append(element_bucket, &v_decodebin[WFD_SINK_V_D_H265_PARSE]);
3900 if (v_decodebin[WFD_SINK_V_D_H265_DEC].gst)
3901 element_bucket = g_list_append(element_bucket, &v_decodebin[WFD_SINK_V_D_H265_DEC]);
3903 case MM_WFD_SINK_VIDEO_CODEC_VP9:
3904 if (v_decodebin[WFD_SINK_V_D_VP9_PARSE].gst)
3905 element_bucket = g_list_append(element_bucket, &v_decodebin[WFD_SINK_V_D_VP9_PARSE]);
3906 if (v_decodebin[WFD_SINK_V_D_VP9_DEC].gst)
3907 element_bucket = g_list_append(element_bucket, &v_decodebin[WFD_SINK_V_D_VP9_DEC]);
3911 wfd_sink_error("video codec is not decied yet. cannot link video decpdebin...");
3916 if (element_bucket == NULL) {
3917 wfd_sink_error("there are no elements to be linked in the video decodebin, destroy it");
3918 if (MM_ERROR_NONE != __mm_wfd_sink_destroy_video_decodebin(wfd_sink)) {
3919 wfd_sink_error("failed to destroy video decodebin");
3925 /* adding elements to video decodebin */
3926 if (!__mm_wfd_sink_gst_element_add_bucket_to_bin(GST_BIN_CAST(v_decodebin[WFD_SINK_V_D_BIN].gst), element_bucket, FALSE)) {
3927 wfd_sink_error("failed to add elements to video decodebin");
3931 /* linking elements in the bucket by added order. */
3932 if (__mm_wfd_sink_gst_element_link_bucket(element_bucket) == -1) {
3933 wfd_sink_error("failed to link elements of the video decodebin");
3937 /* get first element's sinkpad for creating ghostpad */
3938 list_temp = g_list_first(element_bucket);
3939 if (list_temp == NULL) {
3940 wfd_sink_error("failed to get first list of the element_bucket");
3944 first_element = (MMWFDSinkGstElement *)list_temp->data;
3945 if (!first_element) {
3946 wfd_sink_error("failed to get first element of the video decodebin");
3950 sinkpad = gst_element_get_static_pad(GST_ELEMENT(first_element->gst), "sink");
3952 wfd_sink_error("failed to get sink pad from element(%s)",
3953 GST_STR_NULL(GST_ELEMENT_NAME(first_element->gst)));
3957 ghostpad = gst_ghost_pad_new("sink", sinkpad);
3959 wfd_sink_error("failed to create ghostpad of video decodebin");
3963 if (FALSE == gst_element_add_pad(v_decodebin[WFD_SINK_V_D_BIN].gst, ghostpad)) {
3964 wfd_sink_error("failed to add ghostpad to video decodebin");
3967 gst_object_unref(GST_OBJECT(sinkpad));
3971 /* get last element's src for creating ghostpad */
3972 list_temp = g_list_last(element_bucket);
3973 if (list_temp == NULL) {
3974 wfd_sink_error("failed to get last list of the element_bucket");
3978 last_element = (MMWFDSinkGstElement *)list_temp->data;
3979 if (!last_element) {
3980 wfd_sink_error("failed to get last element of the video decodebin");
3984 srcpad = gst_element_get_static_pad(GST_ELEMENT(last_element->gst), "src");
3986 wfd_sink_error("failed to get src pad from element(%s)",
3987 GST_STR_NULL(GST_ELEMENT_NAME(last_element->gst)));
3991 ghostpad = gst_ghost_pad_new("src", srcpad);
3993 wfd_sink_error("failed to create ghostpad of video decodebin");
3997 if (FALSE == gst_element_add_pad(v_decodebin[WFD_SINK_V_D_BIN].gst, ghostpad)) {
3998 wfd_sink_error("failed to add ghostpad to video decodebin");
4001 gst_object_unref(GST_OBJECT(srcpad));
4004 g_list_free(element_bucket);
4007 wfd_sink->video_decodebin_is_linked = TRUE;
4009 wfd_sink_debug_fleave();
4011 return MM_ERROR_NONE;
4015 if (srcpad != NULL) {
4016 gst_object_unref(GST_OBJECT(srcpad));
4020 if (sinkpad != NULL) {
4021 gst_object_unref(GST_OBJECT(sinkpad));
4025 if (element_bucket != NULL) {
4026 g_list_free(element_bucket);
4027 element_bucket = NULL;
4029 return MM_ERROR_WFD_INTERNAL;
4032 static int __mm_wfd_sink_prepare_videodec(mm_wfd_sink_t *wfd_sink, GstElement *video_dec)
4034 wfd_sink_debug_fenter();
4036 /* check video decoder is created */
4037 wfd_sink_return_val_if_fail(video_dec, MM_ERROR_WFD_INVALID_ARGUMENT);
4038 wfd_sink_return_val_if_fail(wfd_sink && wfd_sink->attrs, MM_ERROR_WFD_NOT_INITIALIZED);
4040 wfd_sink_debug_fleave();
4042 return MM_ERROR_NONE;
4045 static int __mm_wfd_sink_prepare_videosink(mm_wfd_sink_t *wfd_sink, GstElement *video_sink)
4047 gboolean visible = TRUE;
4048 gint surface_type = MM_DISPLAY_SURFACE_OVERLAY;
4050 wfd_sink_debug_fenter();
4052 /* check videosink is created */
4053 wfd_sink_return_val_if_fail(video_sink, MM_ERROR_WFD_INVALID_ARGUMENT);
4054 wfd_sink_return_val_if_fail(wfd_sink && wfd_sink->attrs, MM_ERROR_WFD_NOT_INITIALIZED);
4056 /* update display surface */
4057 mm_attrs_get_int_by_name(wfd_sink->attrs, "display_surface_type", &surface_type);
4058 wfd_sink_info("check display surface type attribute: %d", surface_type);
4059 mm_attrs_get_int_by_name(wfd_sink->attrs, "display_visible", &visible);
4060 wfd_sink_info("check display visible attribute: %d", visible);
4062 if (FALSE == visible) {
4063 wfd_sink_info("skipped to prepare video sink. display_visible is FALSE.");
4064 g_object_set(G_OBJECT(video_sink), "visible", visible, NULL);
4065 return MM_ERROR_NONE;
4068 /* configuring display */
4069 switch (surface_type) {
4070 case MM_DISPLAY_SURFACE_EVAS: {
4071 void *object = NULL;
4074 /* common case if using evas surface */
4075 mm_attrs_get_data_by_name(wfd_sink->attrs, "display_overlay", &object);
4076 mm_attrs_get_int_by_name(wfd_sink->attrs, "display_evas_do_scaling", &scaling);
4078 wfd_sink_debug("set video param : evas-object %p", object);
4079 g_object_set(G_OBJECT(video_sink), "evas-object", object, NULL);
4081 wfd_sink_error("no evas object");
4082 return MM_ERROR_WFD_INTERNAL;
4086 case MM_DISPLAY_SURFACE_OVERLAY:
4087 if (USE_EXTERNAL_WL_DISPLAY_HANDLE) {
4088 gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(video_sink),
4089 wfd_sink->display_surface_id);
4091 gst_video_overlay_set_wl_window_wl_surface_id(GST_VIDEO_OVERLAY(video_sink),
4092 wfd_sink->display_surface_id);
4095 case MM_DISPLAY_SURFACE_NULL:
4097 wfd_sink_error("Not Supported Surface.");
4098 return MM_ERROR_WFD_INTERNAL;
4100 wfd_sink_error("Not Supported Surface.(default case)");
4101 return MM_ERROR_WFD_INTERNAL;
4104 g_object_set(G_OBJECT(video_sink), "qos", FALSE, NULL);
4105 g_object_set(G_OBJECT(video_sink), "async", wfd_sink->ini.video_sink_async, NULL);
4106 g_object_set(G_OBJECT(video_sink), "max-lateness", (gint64)wfd_sink->ini.video_sink_max_lateness, NULL);
4107 g_object_set(G_OBJECT(video_sink), "visible", visible, NULL);
4108 g_object_set(G_OBJECT(video_sink), "ts-offset", (gint64)(wfd_sink->ini.sink_ts_offset), NULL);
4110 wfd_sink_debug_fleave();
4112 return MM_ERROR_NONE;
4115 int __mm_wfd_sink_destroy_video_decodebin(mm_wfd_sink_t *wfd_sink)
4117 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
4118 MMWFDSinkGstElement *v_decodebin = NULL;
4119 GstObject *parent = NULL;
4122 wfd_sink_error_fenter();
4124 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
4126 if (wfd_sink->pipeline &&
4127 wfd_sink->pipeline->v_decodebin &&
4128 wfd_sink->pipeline->v_decodebin[WFD_SINK_V_D_BIN].gst) {
4129 v_decodebin = wfd_sink->pipeline->v_decodebin;
4131 wfd_sink_debug("video decodebin is not created, nothing to destroy");
4132 return MM_ERROR_NONE;
4136 parent = gst_element_get_parent(v_decodebin[WFD_SINK_V_D_BIN].gst);
4138 wfd_sink_debug("video decodebin has no parent.. need to relase by itself");
4140 if (GST_STATE(v_decodebin[WFD_SINK_V_D_BIN].gst) >= GST_STATE_READY) {
4141 wfd_sink_error("try to change state of video decodebin to NULL");
4142 ret = gst_element_set_state(v_decodebin[WFD_SINK_V_D_BIN].gst, GST_STATE_NULL);
4143 if (ret != GST_STATE_CHANGE_SUCCESS) {
4144 wfd_sink_error("failed to change state of video decodebin to NULL");
4145 return MM_ERROR_WFD_INTERNAL;
4148 /* release element which are not added to bin */
4149 for (i = 1; i < WFD_SINK_V_D_NUM; i++) { /* NOTE : skip bin */
4150 if (v_decodebin[i].gst) {
4151 parent = gst_element_get_parent(v_decodebin[i].gst);
4153 wfd_sink_debug("unref %s(current ref %d)",
4154 GST_STR_NULL(GST_ELEMENT_NAME(v_decodebin[i].gst)),
4155 ((GObject *) v_decodebin[i].gst)->ref_count);
4156 gst_object_unref(GST_OBJECT(v_decodebin[i].gst));
4157 v_decodebin[i].gst = NULL;
4159 wfd_sink_debug("%s has parent.(current ref %d)",
4160 GST_STR_NULL(GST_ELEMENT_NAME(v_decodebin[i].gst)),
4161 ((GObject *) v_decodebin[i].gst)->ref_count);
4162 gst_object_unref(GST_OBJECT(parent));
4167 /* release video decodebin with it's childs */
4168 if (v_decodebin[WFD_SINK_V_D_BIN].gst) {
4169 wfd_sink_debug("unref %s(current ref %d)",
4170 GST_STR_NULL(GST_ELEMENT_NAME(v_decodebin[WFD_SINK_V_D_BIN].gst)),
4171 ((GObject *)v_decodebin[WFD_SINK_V_D_BIN].gst)->ref_count);
4173 gst_object_unref(GST_OBJECT(v_decodebin[WFD_SINK_V_D_BIN].gst));
4174 v_decodebin[WFD_SINK_V_D_BIN].gst = NULL;
4177 wfd_sink_debug("video decodebin has parent(%s), unref it",
4178 GST_STR_NULL(GST_OBJECT_NAME(GST_OBJECT(parent))));
4180 gst_object_unref(GST_OBJECT(parent));
4184 wfd_sink->video_decodebin_is_linked = FALSE;
4186 MMWFDSINK_FREEIF(wfd_sink->pipeline->v_decodebin);
4188 wfd_sink_error_fleave();
4190 return MM_ERROR_NONE;
4193 int __mm_wfd_sink_create_video_decodebin(mm_wfd_sink_t *wfd_sink)
4195 MMWFDSinkGstElement *v_decodebin = NULL;
4196 guint video_codec = WFD_VIDEO_UNKNOWN;
4197 GList *element_bucket = NULL;
4198 gboolean link = TRUE;
4201 wfd_sink_debug_fenter();
4203 wfd_sink_return_val_if_fail(wfd_sink &&
4205 MM_ERROR_WFD_NOT_INITIALIZED);
4207 if (wfd_sink->pipeline->v_decodebin) {
4208 wfd_sink_debug("video decodebin is already created... nothing to do");
4209 return MM_ERROR_NONE;
4212 if (wfd_sink->stream_info.video_stream_info.codec == MM_WFD_SINK_VIDEO_CODEC_NONE) {
4213 wfd_sink_debug("Skip create video decodebin for none video codec.");
4214 wfd_sink_debug_fleave();
4215 return MM_ERROR_NONE;
4218 /* check video decodebin could be linked now */
4219 switch (wfd_sink->stream_info.video_stream_info.codec) {
4220 case MM_WFD_SINK_VIDEO_CODEC_H264:
4221 video_codec = WFD_VIDEO_H264;
4224 case MM_WFD_SINK_VIDEO_CODEC_H265:
4225 video_codec = WFD_VIDEO_H265;
4228 case MM_WFD_SINK_VIDEO_CODEC_VP9:
4229 video_codec = WFD_VIDEO_VP9;
4232 case MM_WFD_SINK_VIDEO_CODEC_NONE:
4234 wfd_sink_debug("video decodebin could NOT be linked now, just create");
4235 video_codec = wfd_sink->ini.wfd_video_formats.video_codec | wfd_sink->ini.wfd2_video_formats.video_codec;
4241 v_decodebin = (MMWFDSinkGstElement *)g_malloc0(sizeof(MMWFDSinkGstElement) * WFD_SINK_V_D_NUM);
4243 wfd_sink_error("failed to allocate memory for video decodebin");
4244 return MM_ERROR_WFD_NO_FREE_SPACE;
4247 memset(v_decodebin, 0, sizeof(MMWFDSinkGstElement) * WFD_SINK_V_D_NUM);
4249 /* create video decodebin */
4250 v_decodebin[WFD_SINK_V_D_BIN].id = WFD_SINK_V_D_BIN;
4251 v_decodebin[WFD_SINK_V_D_BIN].gst = gst_bin_new("video_decodebin");
4252 if (!v_decodebin[WFD_SINK_V_D_BIN].gst) {
4253 wfd_sink_error("failed to create video decodebin");
4258 MMWFDSINK_CREATE_ELEMENT(v_decodebin, WFD_SINK_V_D_QUEUE, "queue", "video_queue", FALSE);
4259 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_QUEUE].gst, "sink");
4260 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_QUEUE].gst, "src");
4261 if (v_decodebin[WFD_SINK_V_D_QUEUE].gst)
4262 __mm_wfd_sink_prepare_queue(wfd_sink, v_decodebin[WFD_SINK_V_D_QUEUE].gst);
4265 MMWFDSINK_CREATE_ELEMENT(v_decodebin, WFD_SINK_V_D_HDCP, wfd_sink->ini.name_of_video_hdcp, "video_hdcp", FALSE);
4266 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_HDCP].gst, "sink");
4267 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_HDCP].gst, "src");
4269 if (video_codec & WFD_VIDEO_H264) {
4271 MMWFDSINK_CREATE_ELEMENT(v_decodebin, WFD_SINK_V_D_H264_PARSE, wfd_sink->ini.name_of_video_h264_parser, "video_h264_parser", FALSE);
4272 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_H264_PARSE].gst, "sink");
4273 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_H264_PARSE].gst, "src");
4276 MMWFDSINK_CREATE_ELEMENT(v_decodebin, WFD_SINK_V_D_H264_DEC, wfd_sink->ini.name_of_video_h264_decoder, "video_h264_dec", FALSE);
4277 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_H264_DEC].gst, "sink");
4278 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_H264_DEC].gst, "src");
4279 if (v_decodebin[WFD_SINK_V_D_H264_DEC].gst) {
4280 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_videodec(wfd_sink, v_decodebin[WFD_SINK_V_D_H264_DEC].gst)) {
4281 wfd_sink_error("failed to set video decoder property...");
4287 if (video_codec & WFD_VIDEO_H265) {
4289 MMWFDSINK_CREATE_ELEMENT(v_decodebin, WFD_SINK_V_D_H265_PARSE, wfd_sink->ini.name_of_video_h264_parser, "video_h265_parser", FALSE);
4290 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_H265_PARSE].gst, "sink");
4291 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_H265_PARSE].gst, "src");
4294 MMWFDSINK_CREATE_ELEMENT(v_decodebin, WFD_SINK_V_D_H265_DEC, wfd_sink->ini.name_of_video_h265_decoder, "video_h265_dec", FALSE);
4295 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_H265_DEC].gst, "sink");
4296 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_H265_DEC].gst, "src");
4297 if (v_decodebin[WFD_SINK_V_D_H265_DEC].gst) {
4298 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_videodec(wfd_sink, v_decodebin[WFD_SINK_V_D_H265_DEC].gst)) {
4299 wfd_sink_error("failed to set video decoder property...");
4304 if (video_codec & WFD_VIDEO_VP9) {
4306 MMWFDSINK_CREATE_ELEMENT(v_decodebin, WFD_SINK_V_D_VP9_PARSE, wfd_sink->ini.name_of_video_vp9_parser, "video_vp9_parser", FALSE);
4307 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_VP9_PARSE].gst, "sink");
4308 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_VP9_PARSE].gst, "src");
4311 MMWFDSINK_CREATE_ELEMENT(v_decodebin, WFD_SINK_V_D_VP9_DEC, wfd_sink->ini.name_of_video_vp9_decoder, "video_vp9_dec", FALSE);
4312 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_VP9_DEC].gst, "sink");
4313 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_VP9_DEC].gst, "src");
4314 if (v_decodebin[WFD_SINK_V_D_VP9_DEC].gst) {
4315 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_videodec(wfd_sink, v_decodebin[WFD_SINK_V_D_VP9_DEC].gst)) {
4316 wfd_sink_error("failed to set video decoder property...");
4322 g_list_free(element_bucket);
4325 wfd_sink->pipeline->v_decodebin = v_decodebin;
4327 /* link video decodebin if video codec is fixed */
4329 if (MM_ERROR_NONE != __mm_wfd_sink_link_video_decodebin(wfd_sink)) {
4330 wfd_sink_error("failed to link video decodebin, destroy video decodebin");
4331 __mm_wfd_sink_destroy_video_decodebin(wfd_sink);
4332 return MM_ERROR_WFD_INTERNAL;
4336 wfd_sink_debug_fleave();
4338 return MM_ERROR_NONE;
4342 wfd_sink_error("failed to create video decodebin, releasing all");
4344 g_list_free(element_bucket);
4346 /* release element which are not added to bin */
4347 for (i = 1; i < WFD_SINK_V_D_NUM; i++) { /* NOTE : skip bin */
4348 if (v_decodebin != NULL && v_decodebin[i].gst) {
4349 GstObject *parent = NULL;
4350 parent = gst_element_get_parent(v_decodebin[i].gst);
4353 gst_object_unref(GST_OBJECT(v_decodebin[i].gst));
4354 v_decodebin[i].gst = NULL;
4356 gst_object_unref(GST_OBJECT(parent));
4362 /* release video decodebin with it's childs */
4363 if (v_decodebin != NULL && v_decodebin[WFD_SINK_V_D_BIN].gst) {
4364 gst_object_unref(GST_OBJECT(v_decodebin[WFD_SINK_V_D_BIN].gst));
4365 v_decodebin[WFD_SINK_V_D_BIN].gst = NULL;
4368 MMWFDSINK_FREEIF(v_decodebin);
4370 return MM_ERROR_WFD_INTERNAL;
4373 int __mm_wfd_sink_destroy_video_sinkbin(mm_wfd_sink_t *wfd_sink)
4375 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
4376 MMWFDSinkGstElement *v_sinkbin = NULL;
4377 GstObject *parent = NULL;
4380 wfd_sink_error_fenter();
4382 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
4384 if (wfd_sink->pipeline &&
4385 wfd_sink->pipeline->v_sinkbin &&
4386 wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_BIN].gst) {
4387 v_sinkbin = wfd_sink->pipeline->v_sinkbin;
4389 wfd_sink_debug("video sinkbin is not created, nothing to destroy");
4390 return MM_ERROR_NONE;
4394 parent = gst_element_get_parent(v_sinkbin[WFD_SINK_V_S_BIN].gst);
4396 wfd_sink_debug("video sinkbin has no parent.. need to relase by itself");
4398 if (GST_STATE(v_sinkbin[WFD_SINK_V_S_BIN].gst) >= GST_STATE_READY) {
4399 wfd_sink_error("try to change state of video sinkbin to NULL");
4400 ret = gst_element_set_state(v_sinkbin[WFD_SINK_V_S_BIN].gst, GST_STATE_NULL);
4401 if (ret != GST_STATE_CHANGE_SUCCESS) {
4402 wfd_sink_error("failed to change state of video sinkbin to NULL");
4403 return MM_ERROR_WFD_INTERNAL;
4406 /* release element which are not added to bin */
4407 for (i = 1; i < WFD_SINK_V_S_NUM; i++) { /* NOTE : skip bin */
4408 if (v_sinkbin[i].gst) {
4409 parent = gst_element_get_parent(v_sinkbin[i].gst);
4411 wfd_sink_debug("unref %s(current ref %d)",
4412 GST_STR_NULL(GST_ELEMENT_NAME(v_sinkbin[i].gst)),
4413 ((GObject *) v_sinkbin[i].gst)->ref_count);
4414 gst_object_unref(GST_OBJECT(v_sinkbin[i].gst));
4415 v_sinkbin[i].gst = NULL;
4417 wfd_sink_debug("%s has parent.(current ref %d)",
4418 GST_STR_NULL(GST_ELEMENT_NAME(v_sinkbin[i].gst)),
4419 ((GObject *) v_sinkbin[i].gst)->ref_count);
4420 gst_object_unref(GST_OBJECT(parent));
4425 /* release video sinkbin with it's childs */
4426 if (v_sinkbin[WFD_SINK_V_S_BIN].gst) {
4427 gst_object_unref(GST_OBJECT(v_sinkbin[WFD_SINK_V_S_BIN].gst));
4428 v_sinkbin[WFD_SINK_V_S_BIN].gst = NULL;
4431 wfd_sink_debug("video sinkbin has parent(%s), unref it ",
4432 GST_STR_NULL(GST_OBJECT_NAME(GST_OBJECT(parent))));
4434 gst_object_unref(GST_OBJECT(parent));
4438 MMWFDSINK_FREEIF(wfd_sink->pipeline->v_sinkbin);
4440 wfd_sink_error_fleave();
4442 return MM_ERROR_NONE;
4445 int __mm_wfd_sink_create_video_sinkbin(mm_wfd_sink_t *wfd_sink)
4447 MMWFDSinkGstElement *first_element = NULL;
4448 MMWFDSinkGstElement *v_sinkbin = NULL;
4449 GList *element_bucket = NULL;
4451 GstPad *ghostpad = NULL;
4453 gint surface_type = MM_DISPLAY_SURFACE_OVERLAY;
4455 wfd_sink_debug_fenter();
4457 wfd_sink_return_val_if_fail(wfd_sink &&
4459 MM_ERROR_WFD_NOT_INITIALIZED);
4461 if (wfd_sink->pipeline->v_sinkbin != NULL) {
4462 wfd_sink_error("The video sink bin is already created.");
4463 return MM_ERROR_NONE;
4466 if (wfd_sink->stream_info.video_stream_info.codec == MM_WFD_SINK_VIDEO_CODEC_NONE) {
4467 wfd_sink_error("Skip create video sink bin for non video codec.");
4468 wfd_sink_debug_fleave();
4469 return MM_ERROR_NONE;
4473 v_sinkbin = (MMWFDSinkGstElement *)g_malloc0(sizeof(MMWFDSinkGstElement) * WFD_SINK_V_S_NUM);
4475 wfd_sink_error("failed to allocate memory for video sinkbin");
4476 return MM_ERROR_WFD_NO_FREE_SPACE;
4479 memset(v_sinkbin, 0, sizeof(MMWFDSinkGstElement) * WFD_SINK_V_S_NUM);
4481 /* create video sinkbin */
4482 v_sinkbin[WFD_SINK_V_S_BIN].id = WFD_SINK_V_S_BIN;
4483 v_sinkbin[WFD_SINK_V_S_BIN].gst = gst_bin_new("video_sinkbin");
4484 if (!v_sinkbin[WFD_SINK_V_S_BIN].gst) {
4485 wfd_sink_error("failed to create video sinkbin");
4489 /* create convert */
4490 MMWFDSINK_CREATE_ELEMENT(v_sinkbin, WFD_SINK_V_S_CONVERT, wfd_sink->ini.name_of_video_converter, "video_convert", TRUE);
4491 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_sinkbin[WFD_SINK_V_S_CONVERT].gst, "sink");
4492 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_sinkbin[WFD_SINK_V_S_CONVERT].gst, "src");
4495 MMWFDSINK_CREATE_ELEMENT(v_sinkbin, WFD_SINK_V_S_FILTER, wfd_sink->ini.name_of_video_filter, "video_filter", TRUE);
4496 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_sinkbin[WFD_SINK_V_S_FILTER].gst, "sink");
4497 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_sinkbin[WFD_SINK_V_S_FILTER].gst, "src");
4498 if (v_sinkbin[WFD_SINK_V_S_FILTER].gst) {
4499 GstCaps *caps = NULL;
4500 caps = gst_caps_new_simple("video/x-raw", "format", G_TYPE_STRING, "SN12", NULL);
4501 g_object_set(G_OBJECT(v_sinkbin[WFD_SINK_V_S_FILTER].gst), "caps", caps, NULL);
4502 gst_object_unref(GST_OBJECT(caps));
4507 mm_attrs_get_int_by_name(wfd_sink->attrs, "display_surface_type", &surface_type);
4509 if (surface_type == MM_DISPLAY_SURFACE_OVERLAY) {
4510 MMWFDSINK_CREATE_ELEMENT(v_sinkbin, WFD_SINK_V_S_SINK, wfd_sink->ini.name_of_video_sink, "video_sink", TRUE);
4511 } else if (surface_type == MM_DISPLAY_SURFACE_EVAS) {
4512 MMWFDSINK_CREATE_ELEMENT(v_sinkbin, WFD_SINK_V_S_SINK, wfd_sink->ini.name_of_video_evas_sink, "video_sink", TRUE);
4514 wfd_sink_error("failed to set video sink....");
4518 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_sinkbin[WFD_SINK_V_S_SINK].gst, "sink");
4519 if (v_sinkbin[WFD_SINK_V_S_SINK].gst) {
4520 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_videosink(wfd_sink, v_sinkbin[WFD_SINK_V_S_SINK].gst)) {
4521 wfd_sink_error("failed to set video sink property....");
4526 /* adding created elements to video sinkbin */
4527 if (!__mm_wfd_sink_gst_element_add_bucket_to_bin(GST_BIN_CAST(v_sinkbin[WFD_SINK_V_S_BIN].gst), element_bucket, FALSE)) {
4528 wfd_sink_error("failed to add elements to video sinkbin");
4532 /* linking elements in the bucket by added order. */
4533 if (__mm_wfd_sink_gst_element_link_bucket(element_bucket) == -1) {
4534 wfd_sink_error("failed to link elements of the video sinkbin");
4538 /* get first element's sinkpad for creating ghostpad */
4539 first_element = (MMWFDSinkGstElement *)g_list_nth_data(element_bucket, 0);
4540 if (!first_element) {
4541 wfd_sink_error("failed to get first element of the video sinkbin");
4545 pad = gst_element_get_static_pad(GST_ELEMENT(first_element->gst), "sink");
4547 wfd_sink_error("failed to get pad from first element(%s) of the video sinkbin",
4548 GST_STR_NULL(GST_ELEMENT_NAME(first_element->gst)));
4552 ghostpad = gst_ghost_pad_new("sink", pad);
4554 wfd_sink_error("failed to create ghostpad of the video sinkbin");
4558 if (FALSE == gst_element_add_pad(v_sinkbin[WFD_SINK_V_S_BIN].gst, ghostpad)) {
4559 wfd_sink_error("failed to add ghostpad to video sinkbin");
4563 gst_object_unref(GST_OBJECT(pad));
4566 g_list_free(element_bucket);
4567 element_bucket = NULL;
4570 wfd_sink->pipeline->v_sinkbin = v_sinkbin;
4572 wfd_sink_debug_fleave();
4574 return MM_ERROR_NONE;
4578 wfd_sink_error("failed to create video sinkbin, releasing all");
4581 gst_object_unref(GST_OBJECT(pad));
4586 gst_object_unref(GST_OBJECT(ghostpad));
4590 if (element_bucket) {
4591 g_list_free(element_bucket);
4592 element_bucket = NULL;
4595 /* release element which are not added to bin */
4596 for (i = 1; i < WFD_SINK_V_S_NUM; i++) { /* NOTE : skip bin */
4597 if (v_sinkbin != NULL && v_sinkbin[i].gst) {
4598 GstObject *parent = NULL;
4599 parent = gst_element_get_parent(v_sinkbin[i].gst);
4602 gst_object_unref(GST_OBJECT(v_sinkbin[i].gst));
4603 v_sinkbin[i].gst = NULL;
4605 gst_object_unref(GST_OBJECT(parent));
4611 /* release video sinkbin with it's childs */
4612 if (v_sinkbin != NULL && v_sinkbin[WFD_SINK_V_S_BIN].gst) {
4613 gst_object_unref(GST_OBJECT(v_sinkbin[WFD_SINK_V_S_BIN].gst));
4614 v_sinkbin[WFD_SINK_V_S_BIN].gst = NULL;
4616 MMWFDSINK_FREEIF(v_sinkbin);
4618 return MM_ERROR_WFD_INTERNAL;
4621 int __mm_wfd_sink_destroy_pipeline(mm_wfd_sink_t *wfd_sink)
4623 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
4626 wfd_sink_debug_fenter();
4628 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
4630 /* cleanup gst stuffs */
4631 if (wfd_sink->pipeline) {
4632 MMWFDSinkGstElement *mainbin = wfd_sink->pipeline->mainbin;
4636 PRINT_WFD_REF_COUNT(wfd_sink);
4638 ret = gst_element_set_state(mainbin[WFD_SINK_M_PIPE].gst, GST_STATE_NULL);
4639 if (ret != GST_STATE_CHANGE_SUCCESS) {
4640 wfd_sink_error("failed to change state of pipeline to NULL. ret[%d]", ret);
4641 return MM_ERROR_WFD_INTERNAL;
4643 wfd_sink_debug("Successed to change state of pipeline to NULL");
4646 bus = gst_pipeline_get_bus(GST_PIPELINE(mainbin[WFD_SINK_M_PIPE].gst));
4648 GstMessage *gst_msg = NULL;
4649 while ((gst_msg = gst_bus_pop(bus)) != NULL) {
4650 _mm_wfd_sink_msg_callback(bus, gst_msg, (gpointer)wfd_sink);
4651 gst_message_unref(gst_msg);
4654 gst_object_unref(bus);
4658 PRINT_WFD_REF_COUNT(wfd_sink);
4660 if (MM_ERROR_NONE != __mm_wfd_sink_destroy_video_decodebin(wfd_sink)) {
4661 wfd_sink_error("failed to destroy video decodebin");
4662 return MM_ERROR_WFD_INTERNAL;
4665 if (MM_ERROR_NONE != __mm_wfd_sink_destroy_audio_decodebin(wfd_sink)) {
4666 wfd_sink_error("failed to destroy audio decodebin");
4667 return MM_ERROR_WFD_INTERNAL;
4670 if (MM_ERROR_NONE != __mm_wfd_sink_destroy_video_sinkbin(wfd_sink)) {
4671 wfd_sink_error("failed to destroy video sinkbin");
4672 return MM_ERROR_WFD_INTERNAL;
4675 if (MM_ERROR_NONE != __mm_wfd_sink_destroy_audio_sinkbin(wfd_sink)) {
4676 wfd_sink_error("failed to destroy audio sinkbin");
4677 return MM_ERROR_WFD_INTERNAL;
4680 gst_object_unref(GST_OBJECT(mainbin[WFD_SINK_M_PIPE].gst));
4681 mainbin[WFD_SINK_M_PIPE].gst = NULL;
4683 MMWFDSINK_FREEIF(mainbin);
4686 MMWFDSINK_FREEIF(wfd_sink->pipeline);
4689 if (wfd_sink->msg_callback_id > 0) {
4690 g_source_remove(wfd_sink->msg_callback_id);
4691 wfd_sink->msg_callback_id = 0;
4694 wfd_sink->audio_decodebin_is_linked = FALSE;
4695 wfd_sink->video_decodebin_is_linked = FALSE;
4696 wfd_sink->need_to_reset_basetime = FALSE;
4697 wfd_sink->supportive_resolution = MM_WFD_SINK_RESOLUTION_UNKNOWN;
4699 wfd_sink_debug_fleave();
4701 return MM_ERROR_NONE;
4705 __mm_wfd_sink_dump_pipeline_state(mm_wfd_sink_t *wfd_sink)
4707 GstIterator *iter = NULL;
4708 gboolean done = FALSE;
4710 GstElement *item = NULL;
4711 GstElementFactory *factory = NULL;
4713 GstState state = GST_STATE_VOID_PENDING;
4714 GstState pending = GST_STATE_VOID_PENDING;
4715 GstClockTime time = 200 * GST_MSECOND;
4717 wfd_sink_debug_fenter();
4719 wfd_sink_return_if_fail(wfd_sink &&
4720 wfd_sink->pipeline &&
4721 wfd_sink->pipeline->mainbin &&
4722 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst);
4724 iter = gst_bin_iterate_recurse(GST_BIN(wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst));
4728 switch (gst_iterator_next(iter, (gpointer)&item)) {
4729 case GST_ITERATOR_OK:
4730 gst_element_get_state(GST_ELEMENT(item), &state, &pending, time);
4732 factory = gst_element_get_factory(item) ;
4734 wfd_sink_error("%s:%s : From:%s To:%s refcount : %d",
4735 GST_STR_NULL(GST_OBJECT_NAME(factory)),
4736 GST_STR_NULL(GST_ELEMENT_NAME(item)),
4737 gst_element_state_get_name(state),
4738 gst_element_state_get_name(pending),
4739 GST_OBJECT_REFCOUNT_VALUE(item));
4741 gst_object_unref(item);
4744 case GST_ITERATOR_RESYNC:
4745 gst_iterator_resync(iter);
4747 case GST_ITERATOR_ERROR:
4750 case GST_ITERATOR_DONE:
4760 item = GST_ELEMENT_CAST(wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst);
4762 gst_element_get_state(GST_ELEMENT(item), &state, &pending, time);
4764 factory = gst_element_get_factory(item) ;
4766 wfd_sink_error("%s:%s : From:%s To:%s refcount : %d",
4767 GST_OBJECT_NAME(factory),
4768 GST_ELEMENT_NAME(item),
4769 gst_element_state_get_name(state),
4770 gst_element_state_get_name(pending),
4771 GST_OBJECT_REFCOUNT_VALUE(item));
4775 gst_iterator_free(iter);
4777 wfd_sink_debug_fleave();
4783 _mm_wfds_sink_get_state_name(MMWFDSinkStateType state)
4786 case MM_WFD_SINK_STATE_NONE:
4788 case MM_WFD_SINK_STATE_NULL:
4790 case MM_WFD_SINK_STATE_PREPARED:
4792 case MM_WFD_SINK_STATE_CONNECTED:
4794 case MM_WFD_SINK_STATE_PLAYING:
4796 case MM_WFD_SINK_STATE_PAUSED:
4798 case MM_WFD_SINK_STATE_DISCONNECTED:
4799 return "DISCONNECTED";
4805 int _mm_wfd_sink_set_resolution(mm_wfd_sink_t *wfd_sink, MMWFDSinkResolution resolution)
4807 MMWFDSinkStateType cur_state = MM_WFD_SINK_STATE_NONE;
4809 wfd_sink_debug_fenter();
4811 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
4813 MMWFDSINK_PRINT_STATE(wfd_sink);
4814 cur_state = MMWFDSINK_CURRENT_STATE(wfd_sink);
4815 if (cur_state != MM_WFD_SINK_STATE_NULL) {
4816 wfd_sink_error("This function must be called when MM_WFD_SINK_STATE_NULL");
4817 return MM_ERROR_WFD_INVALID_STATE;
4820 wfd_sink->supportive_resolution = resolution;
4822 wfd_sink_debug_fleave();
4824 return MM_ERROR_NONE;
4827 int _mm_wfd_sink_set_display_overlay(mm_wfd_sink_t *wfd_sink, void *display_object)
4829 int wl_surface_id = 0;
4830 static void *display_overlay = NULL;
4831 int wl_window_x = 0;
4832 int wl_window_y = 0;
4833 int wl_window_width = 0;
4834 int wl_window_height = 0;
4835 struct wl_surface *wl_surface = NULL;
4836 struct wl_display *wl_display = NULL;
4837 Ecore_Wl2_Window *wl2_window = NULL;
4838 Ecore_Wl2_Display *wl2_display = NULL;
4839 wl_client *wlclient = NULL;
4840 Evas_Object *obj = NULL;
4841 void *object = display_object;
4842 const char *object_type = NULL;
4844 pid_t pid = getpid();
4845 pid_t tid = syscall(SYS_gettid);
4848 wfd_sink_error("API isn't called in main thread");
4849 return MM_ERROR_WFD_INTERNAL;
4852 if (object == NULL) {
4853 wfd_sink_debug("display object is NULL!");
4854 return MM_ERROR_WFD_INTERNAL;
4856 obj = (Evas_Object *)object;
4857 object_type = evas_object_type_get(obj);
4858 wfd_sink_debug("window object type : %s", object_type);
4860 if (strcmp(object_type, "elm_win")) {
4861 wfd_sink_error("Window type is not elm_win");
4862 return MM_ERROR_WFD_INTERNAL;
4865 /* wayland overlay surface */
4866 wfd_sink_info("Wayland overlay surface type");
4867 evas_object_geometry_get(obj, &wl_window_x, &wl_window_y, &wl_window_width, &wl_window_height);
4869 wfd_sink_debug("x[%d] y[%d] width[%d] height[%d]", wl_window_x, wl_window_y,
4870 wl_window_width, wl_window_height);
4872 wl2_window = ecore_evas_wayland2_window_get(ecore_evas_ecore_evas_get(evas_object_evas_get(obj)));
4873 ecore_wl2_window_video_has(wl2_window, EINA_TRUE);
4874 wl_surface = ecore_wl2_window_surface_get(wl2_window);
4876 /* get wl_display */
4877 wl2_display = ecore_wl2_connected_display_get(NULL);
4878 wl_display = ecore_wl2_display_get(wl2_display);
4880 wfd_sink_debug("previous display object : %p current object : %p", display_overlay, object);
4881 if (wl_surface && wl_display && (wl_surface_id == 0 || display_overlay != object)) {
4882 wfd_sink_debug("surface = %p, wl_display = %p", wl_surface, wl_display);
4883 display_overlay = object;
4885 ret = mm_wfd_sink_wlclient_create(&wlclient);
4886 if (ret != MM_ERROR_NONE) {
4887 wfd_sink_error("Wayland client create failure");
4888 return MM_ERROR_WFD_INTERNAL;
4890 wfd_sink_debug("Try to get surface id");
4892 wl_surface_id = mm_wfd_sink_wlclient_get_wl_window_wl_surface_id(wlclient, wl_surface, wl_display);
4894 wfd_sink_debug("wl_surface_id = %d", wl_surface_id);
4895 if (wl_surface_id == 0) {
4896 wfd_sink_error("wl_surface_id cannot be zero.");
4897 return MM_ERROR_WFD_INTERNAL;
4899 wfd_sink->display_surface_id = wl_surface_id;
4906 return MM_ERROR_NONE;
4909 void __mm_wfd_sink_print_ref_count(mm_wfd_sink_t *wfd_sink)
4912 MMWFDSinkGstElement *mainbin = NULL;
4913 MMWFDSinkGstElement *v_decodebin = NULL;
4914 MMWFDSinkGstElement *a_decodebin = NULL;
4915 MMWFDSinkGstElement *v_sinkbin = NULL;
4916 MMWFDSinkGstElement *a_sinkbin = NULL;
4918 wfd_sink_debug_fenter();
4920 wfd_sink_return_if_fail(wfd_sink);
4921 wfd_sink_return_if_fail(wfd_sink->pipeline);
4923 wfd_sink_debug("************* wfd pipeline ref count start *************");
4924 wfd_sink_debug("try to check mainbin");
4926 if (wfd_sink->pipeline->mainbin &&
4927 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst) {
4928 mainbin = wfd_sink->pipeline->mainbin;
4930 for (i = 0; i < WFD_SINK_M_NUM; i++) {
4931 if (mainbin[i].gst) {
4932 wfd_sink_debug("%s(current ref %d)",
4933 GST_ELEMENT_NAME(mainbin[i].gst),
4934 GST_OBJECT_REFCOUNT(mainbin[i].gst));
4939 wfd_sink_debug("try to check a_decodebin");
4941 if (wfd_sink->pipeline->a_decodebin &&
4942 wfd_sink->pipeline->a_decodebin[WFD_SINK_A_D_BIN].gst) {
4943 a_decodebin = wfd_sink->pipeline->a_decodebin;
4945 for (i = 0; i < WFD_SINK_A_D_NUM; i++) {
4946 if (a_decodebin[i].gst) {
4947 wfd_sink_debug("%s(current ref %d)",
4948 GST_ELEMENT_NAME(a_decodebin[i].gst),
4949 GST_OBJECT_REFCOUNT(a_decodebin[i].gst));
4954 wfd_sink_debug("try to check a_sinkbin");
4956 if (wfd_sink->pipeline->a_sinkbin &&
4957 wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_BIN].gst) {
4958 a_sinkbin = wfd_sink->pipeline->a_sinkbin;
4960 for (i = 0; i < WFD_SINK_A_S_NUM; i++) {
4961 if (a_sinkbin[i].gst) {
4962 wfd_sink_debug("%s(current ref %d)",
4963 GST_ELEMENT_NAME(a_sinkbin[i].gst),
4964 GST_OBJECT_REFCOUNT(a_sinkbin[i].gst));
4969 wfd_sink_debug("try to check v_decodebin");
4971 if (wfd_sink->pipeline->v_decodebin &&
4972 wfd_sink->pipeline->v_decodebin[WFD_SINK_V_D_BIN].gst) {
4973 v_decodebin = wfd_sink->pipeline->v_decodebin;
4975 for (i = 0; i < WFD_SINK_V_D_NUM; i++) {
4976 if (v_decodebin[i].gst) {
4977 wfd_sink_debug("%s(current ref %d)",
4978 GST_ELEMENT_NAME(v_decodebin[i].gst),
4979 GST_OBJECT_REFCOUNT(v_decodebin[i].gst));
4984 wfd_sink_debug("try to check v_sinkbin");
4986 if (wfd_sink->pipeline->v_sinkbin &&
4987 wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_BIN].gst) {
4988 v_sinkbin = wfd_sink->pipeline->v_sinkbin;
4990 for (i = 0; i < WFD_SINK_V_S_NUM; i++) {
4991 if (v_sinkbin[i].gst) {
4992 wfd_sink_debug("%s(current ref %d)",
4993 GST_ELEMENT_NAME(v_sinkbin[i].gst),
4994 GST_OBJECT_REFCOUNT(v_sinkbin[i].gst));
4998 wfd_sink_debug("************* wfd pipeline ref count end *************");
4999 wfd_sink_debug_fleave();