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_Wayland.h>
28 #include "mm_wfd_sink_util.h"
29 #include "mm_wfd_sink_priv.h"
30 #include "mm_wfd_sink_manager.h"
31 #include "mm_wfd_sink_dlog.h"
32 #include "mm_wfd_sink_enum.h"
33 #include "mm_wfd_sink_wayland.h"
34 #include "mm_wfd_sink_config.h"
36 #define PRINT_WFD_REF_COUNT(wfd_sink)\
38 wfd_sink_debug("PRINT WFD REF COUNT");\
39 __mm_wfd_sink_print_ref_count(wfd_sink);\
43 static int __mm_wfd_sink_init_gstreamer(mm_wfd_sink_t *wfd_sink);
44 static int __mm_wfd_sink_create_pipeline(mm_wfd_sink_t *wfd_sink);
45 static int __mm_wfd_sink_destroy_audio_decodebin(mm_wfd_sink_t *wfd_sink);
46 static int __mm_wfd_sink_destroy_video_decodebin(mm_wfd_sink_t *wfd_sink);
47 static int __mm_wfd_sink_destroy_audio_sinkbin(mm_wfd_sink_t *wfd_sink);
48 static int __mm_wfd_sink_destroy_video_sinkbin(mm_wfd_sink_t *wfd_sink);
49 static int __mm_wfd_sink_destroy_pipeline(mm_wfd_sink_t *wfd_sink);
50 static int __mm_wfd_sink_set_pipeline_state(mm_wfd_sink_t *wfd_sink, GstState state, gboolean async);
51 static gboolean _mm_wfd_sink_msg_callback(GstBus *bus, GstMessage *msg, gpointer data);
54 static int __mm_wfd_sink_set_state(mm_wfd_sink_t *wfd_sink, MMWFDSinkStateType state);
57 static void __mm_wfd_sink_dump_pipeline_state(mm_wfd_sink_t *wfd_sink);
59 int _mm_wfd_sink_create(mm_wfd_sink_t **wfd_sink, const char *ini_path)
61 int result = MM_ERROR_NONE;
63 wfd_sink_debug_fenter();
65 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
66 if (ini_path == NULL) {
67 ini_path = MM_WFD_SINK_INI_DEFAULT_PATH;
68 wfd_sink_debug("The wfd ini file path is set as defalut[%s]", ini_path);
71 mm_wfd_sink_t *new_wfd_sink = NULL;
74 new_wfd_sink = g_malloc0(sizeof(mm_wfd_sink_t));
76 wfd_sink_error("failed to allocate memory for wi-fi display sink");
77 return MM_ERROR_WFD_NO_FREE_SPACE;
80 /* Initialize gstreamer related */
81 new_wfd_sink->attrs = 0;
83 new_wfd_sink->pipeline = NULL;
84 new_wfd_sink->audio_decodebin_is_linked = FALSE;
85 new_wfd_sink->video_decodebin_is_linked = FALSE;
87 /* Initialize timestamp compensation related */
88 new_wfd_sink->need_to_reset_basetime = FALSE;
89 new_wfd_sink->clock = NULL;
90 new_wfd_sink->video_buffer_count = 0LL;
91 new_wfd_sink->video_average_gap = 0LL;
92 new_wfd_sink->video_accumulated_gap = 0LL;
93 new_wfd_sink->audio_buffer_count = 0LL;
94 new_wfd_sink->audio_average_gap = 0LL;
95 new_wfd_sink->audio_accumulated_gap = 0LL;
96 new_wfd_sink->last_buffer_timestamp = GST_CLOCK_TIME_NONE;
98 /* Initialize all states */
99 MMWFDSINK_CURRENT_STATE(new_wfd_sink) = MM_WFD_SINK_STATE_NONE;
100 MMWFDSINK_PREVIOUS_STATE(new_wfd_sink) = MM_WFD_SINK_STATE_NONE;
101 MMWFDSINK_PENDING_STATE(new_wfd_sink) = MM_WFD_SINK_STATE_NONE;
103 /* initialize audio/video information */
104 new_wfd_sink->stream_info.audio_stream_info.codec = MM_WFD_SINK_AUDIO_CODEC_NONE;
105 new_wfd_sink->stream_info.audio_stream_info.channels = 0;
106 new_wfd_sink->stream_info.audio_stream_info.sample_rate = 0;
107 new_wfd_sink->stream_info.audio_stream_info.bitwidth = 0;
108 new_wfd_sink->stream_info.video_stream_info.codec = MM_WFD_SINK_VIDEO_CODEC_NONE;
109 new_wfd_sink->stream_info.video_stream_info.width = 0;
110 new_wfd_sink->stream_info.video_stream_info.height = 0;
111 new_wfd_sink->stream_info.video_stream_info.frame_rate = 0;
113 /* Initialize command */
114 new_wfd_sink->cmd = MM_WFD_SINK_COMMAND_CREATE;
115 new_wfd_sink->waiting_cmd = FALSE;
117 /* Initialize manager related */
118 new_wfd_sink->manager_thread = NULL;
119 new_wfd_sink->manager_thread_cmd = NULL;
120 new_wfd_sink->manager_thread_exit = FALSE;
122 /* Initialize video resolution */
123 new_wfd_sink->supportive_resolution = MM_WFD_SINK_RESOLUTION_UNKNOWN;
125 /* construct attributes */
126 new_wfd_sink->attrs = _mmwfd_construct_attribute((MMHandleType)new_wfd_sink);
127 if (!new_wfd_sink->attrs) {
128 MMWFDSINK_FREEIF(new_wfd_sink);
129 wfd_sink_error("failed to set attribute");
130 return MM_ERROR_WFD_INTERNAL;
133 /* load ini for initialize */
134 result = mm_wfd_sink_ini_load(&new_wfd_sink->ini, ini_path);
135 if (result != MM_ERROR_NONE) {
136 wfd_sink_error("failed to load ini file[%s]", ini_path);
137 goto fail_to_load_ini;
139 new_wfd_sink->need_to_reset_basetime = new_wfd_sink->ini.enable_reset_basetime;
141 /* initialize manager */
142 result = _mm_wfd_sink_init_manager(new_wfd_sink);
143 if (result < MM_ERROR_NONE) {
144 wfd_sink_error("failed to init manager : %d", result);
148 /* initialize gstreamer */
149 result = __mm_wfd_sink_init_gstreamer(new_wfd_sink);
150 if (result < MM_ERROR_NONE) {
151 wfd_sink_error("failed to init gstreamer : %d", result);
156 __mm_wfd_sink_set_state(new_wfd_sink, MM_WFD_SINK_STATE_NULL);
158 /* now take handle */
159 *wfd_sink = new_wfd_sink;
161 wfd_sink_debug_fleave();
167 mm_wfd_sink_ini_unload(&new_wfd_sink->ini);
169 _mmwfd_deconstruct_attribute(new_wfd_sink->attrs);
170 MMWFDSINK_FREEIF(new_wfd_sink);
177 int _mm_wfd_sink_prepare(mm_wfd_sink_t *wfd_sink)
179 int result = MM_ERROR_NONE;
181 wfd_sink_debug_fenter();
183 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
185 /* check current wi-fi display sink state */
186 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_PREPARE);
188 /* construct pipeline */
189 /* create main pipeline */
190 result = __mm_wfd_sink_create_pipeline(wfd_sink);
191 if (result < MM_ERROR_NONE) {
192 wfd_sink_error("failed to create pipeline : %d", result);
196 wfd_sink_info("Current session mode : %d", wfd_sink->ini.sink_session_mode);
197 if (wfd_sink->ini.sink_session_mode & (WFD_SESSION_MODE_MIXED | WFD_SESSION_MODE_VIDEO_ONLY)) {
198 /* create video decodebin */
199 result = __mm_wfd_sink_create_video_decodebin(wfd_sink);
200 if (result < MM_ERROR_NONE) {
201 wfd_sink_error("failed to create video decodebin %d", result);
205 /* create video sinkbin */
206 result = __mm_wfd_sink_create_video_sinkbin(wfd_sink);
207 if (result < MM_ERROR_NONE) {
208 wfd_sink_error("failed to create video sinkbin %d", result);
213 /* create audio decodebin */
214 result = __mm_wfd_sink_create_audio_decodebin(wfd_sink);
215 if (result < MM_ERROR_NONE) {
216 wfd_sink_error("fail to create audio decodebin : %d", result);
220 /* create audio sinkbin */
221 result = __mm_wfd_sink_create_audio_sinkbin(wfd_sink);
222 if (result < MM_ERROR_NONE) {
223 wfd_sink_error("fail to create audio sinkbin : %d", result);
227 /* set pipeline READY state */
228 result = __mm_wfd_sink_set_pipeline_state(wfd_sink, GST_STATE_READY, TRUE);
229 if (result < MM_ERROR_NONE) {
230 wfd_sink_error("failed to set state : %d", result);
235 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_PREPARED);
237 wfd_sink_debug_fleave();
243 /* need to destroy pipeline already created */
244 __mm_wfd_sink_destroy_pipeline(wfd_sink);
248 int _mm_wfd_sink_connect(mm_wfd_sink_t *wfd_sink, const char *uri)
250 int result = MM_ERROR_NONE;
252 wfd_sink_debug_fenter();
254 wfd_sink_return_val_if_fail(uri && strlen(uri) > strlen("rtsp://"),
255 MM_ERROR_WFD_INVALID_ARGUMENT);
256 wfd_sink_return_val_if_fail(wfd_sink &&
257 wfd_sink->pipeline &&
258 wfd_sink->pipeline->mainbin &&
259 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst &&
260 wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst &&
261 wfd_sink->pipeline->mainbin[WFD_SINK_M_DEPAY].gst &&
262 wfd_sink->pipeline->mainbin[WFD_SINK_M_DEMUX].gst,
263 MM_ERROR_WFD_NOT_INITIALIZED);
265 /* check current wi-fi display sink state */
266 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_CONNECT);
268 wfd_sink_debug("try to connect to %s.....", GST_STR_NULL(uri));
270 /* set uri to wfdsrc */
271 g_object_set(G_OBJECT(wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst), "location", uri, NULL);
273 /* set pipeline PAUSED state */
274 result = __mm_wfd_sink_set_pipeline_state(wfd_sink, GST_STATE_PAUSED, TRUE);
275 if (result < MM_ERROR_NONE) {
276 wfd_sink_error("failed to set state : %d", result);
280 wfd_sink_debug_fleave();
285 int _mm_wfd_sink_start(mm_wfd_sink_t *wfd_sink)
287 int result = MM_ERROR_NONE;
289 wfd_sink_debug_fenter();
291 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
293 /* check current wi-fi display sink state */
294 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_START);
296 WFD_SINK_MANAGER_LOCK(wfd_sink) ;
297 wfd_sink_debug("check pipeline is ready to start");
298 WFD_SINK_MANAGER_UNLOCK(wfd_sink);
300 result = __mm_wfd_sink_set_pipeline_state(wfd_sink, GST_STATE_PLAYING, TRUE);
301 if (result < MM_ERROR_NONE) {
302 wfd_sink_error("failed to set state : %d", result);
306 wfd_sink_debug_fleave();
311 int _mm_wfd_sink_pause(mm_wfd_sink_t *wfd_sink)
313 int result = MM_ERROR_NONE;
315 wfd_sink_debug_fenter();
317 wfd_sink_return_val_if_fail(wfd_sink &&
318 wfd_sink->pipeline &&
319 wfd_sink->pipeline->mainbin &&
320 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst &&
321 wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst,
322 MM_ERROR_WFD_NOT_INITIALIZED);
324 /* check current wi-fi display sink state */
325 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_PAUSE);
327 g_signal_emit_by_name(wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst, "request-pause", NULL);
329 wfd_sink_debug_fleave();
334 int _mm_wfd_sink_resume(mm_wfd_sink_t *wfd_sink)
336 int result = MM_ERROR_NONE;
338 wfd_sink_debug_fenter();
340 wfd_sink_return_val_if_fail(wfd_sink &&
341 wfd_sink->pipeline &&
342 wfd_sink->pipeline->mainbin &&
343 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst &&
344 wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst,
345 MM_ERROR_WFD_NOT_INITIALIZED);
347 /* check current wi-fi display sink state */
348 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_RESUME);
350 g_signal_emit_by_name(wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst, "request-resume", NULL);
352 wfd_sink_debug_fleave();
357 int _mm_wfd_sink_disconnect(mm_wfd_sink_t *wfd_sink)
359 int result = MM_ERROR_NONE;
361 wfd_sink_debug_fenter();
363 wfd_sink_return_val_if_fail(wfd_sink &&
364 wfd_sink->pipeline &&
365 wfd_sink->pipeline->mainbin &&
366 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst &&
367 wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst,
368 MM_ERROR_WFD_NOT_INITIALIZED);
370 /* check current wi-fi display sink state */
371 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_DISCONNECT);
373 WFD_SINK_MANAGER_APPEND_CMD(wfd_sink, WFD_SINK_MANAGER_CMD_EXIT);
374 WFD_SINK_MANAGER_SIGNAL_CMD(wfd_sink);
376 g_signal_emit_by_name(wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst, "request-close", NULL);
378 wfd_sink_debug_fleave();
383 int _mm_wfd_sink_unprepare(mm_wfd_sink_t *wfd_sink)
385 int result = MM_ERROR_NONE;
387 wfd_sink_debug_fenter();
389 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
391 /* check current wi-fi display sink state */
392 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_UNPREPARE);
394 WFD_SINK_MANAGER_APPEND_CMD(wfd_sink, WFD_SINK_MANAGER_CMD_EXIT);
395 WFD_SINK_MANAGER_SIGNAL_CMD(wfd_sink);
397 /* release pipeline */
398 result = __mm_wfd_sink_destroy_pipeline(wfd_sink);
399 if (result != MM_ERROR_NONE) {
400 wfd_sink_error("failed to destory pipeline");
401 return MM_ERROR_WFD_INTERNAL;
403 wfd_sink_debug("success to destory pipeline");
407 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_NULL);
409 wfd_sink_debug_fleave();
414 int _mm_wfd_sink_destroy(mm_wfd_sink_t *wfd_sink)
416 int result = MM_ERROR_NONE;
418 wfd_sink_debug_fenter();
420 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
422 /* check current wi-fi display sink state */
423 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_DESTROY);
426 mm_wfd_sink_ini_unload(&wfd_sink->ini);
428 /* release attributes */
429 _mmwfd_deconstruct_attribute(wfd_sink->attrs);
431 /* release manager thread */
432 if (MM_ERROR_NONE != _mm_wfd_sink_release_manager(wfd_sink)) {
433 wfd_sink_error("failed to release manager");
434 return MM_ERROR_WFD_INTERNAL;
438 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_NONE);
440 wfd_sink_debug_fleave();
445 int _mm_wfd_set_message_callback(mm_wfd_sink_t *wfd_sink, MMWFDMessageCallback callback, void *user_data)
447 int result = MM_ERROR_NONE;
449 wfd_sink_debug_fenter();
451 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
453 wfd_sink->msg_cb = callback;
454 wfd_sink->msg_user_data = user_data;
456 wfd_sink_debug_fleave();
461 static int __mm_wfd_sink_init_gstreamer(mm_wfd_sink_t *wfd_sink)
463 int result = MM_ERROR_NONE;
466 static const int max_argc = 50;
470 wfd_sink_debug_fenter();
473 argc = calloc(1, sizeof(gint));
474 argv = calloc(max_argc, sizeof(gchar *));
475 if (!argc || !argv) {
476 wfd_sink_error("failed to allocate memory for wfdsink");
478 MMWFDSINK_FREEIF(argv);
479 MMWFDSINK_FREEIF(argc);
481 return MM_ERROR_WFD_NO_FREE_SPACE;
484 /* we would not do fork for scanning plugins */
485 argv[*argc] = g_strdup("--gst-disable-registry-fork");
488 /* check disable registry scan */
489 argv[*argc] = g_strdup("--gst-disable-registry-update");
492 /* check disable segtrap */
493 argv[*argc] = g_strdup("--gst-disable-segtrap");
497 for (i = 0; i < 5; i++) {
498 if (strlen(wfd_sink->ini.gst_param[i]) > 2) {
499 wfd_sink_debug("set %s", wfd_sink->ini.gst_param[i]);
500 argv[*argc] = g_strdup(wfd_sink->ini.gst_param[i]);
505 wfd_sink_debug("initializing gstreamer with following parameter");
506 wfd_sink_debug("argc : %d", *argc);
508 for (i = 0; i < *argc; i++)
509 wfd_sink_debug("argv[%d] : %s", i, argv[i]);
511 /* initializing gstreamer */
512 if (!gst_init_check(argc, &argv, &err)) {
513 wfd_sink_error("failed to initialize gstreamer: %s",
514 err ? err->message : "unknown error occurred");
518 result = MM_ERROR_WFD_INTERNAL;
522 for (i = 0; i < *argc; i++)
523 MMWFDSINK_FREEIF(argv[i]);
525 MMWFDSINK_FREEIF(argv);
526 MMWFDSINK_FREEIF(argc);
528 wfd_sink_debug_fleave();
533 static GstBusSyncReply
534 _mm_wfd_bus_sync_callback(GstBus *bus, GstMessage *message, gpointer data)
536 GstBusSyncReply ret = GST_BUS_PASS;
538 wfd_sink_return_val_if_fail(message &&
539 GST_IS_MESSAGE(message) &&
540 GST_MESSAGE_SRC(message),
543 wfd_sink_debug("get message %p, %s from %p, %s", message,
544 GST_MESSAGE_TYPE_NAME(message), GST_MESSAGE_SRC(message), GST_MESSAGE_SRC_NAME(message));
546 switch (GST_MESSAGE_TYPE(message)) {
547 case GST_MESSAGE_TAG:
549 case GST_MESSAGE_DURATION:
551 case GST_MESSAGE_STATE_CHANGED:
552 /* we only handle state change messages from pipeline */
553 if (GST_IS_PIPELINE(GST_MESSAGE_SRC(message)))
554 _mm_wfd_sink_msg_callback(bus, message, data);
558 case GST_MESSAGE_ASYNC_DONE:
559 if (GST_IS_PIPELINE(GST_MESSAGE_SRC(message)))
560 _mm_wfd_sink_msg_callback(bus, message, data);
568 if (ret == GST_BUS_DROP) {
569 gst_message_unref(message);
576 int __mm_wfd_sink_activate_audio_stream(mm_wfd_sink_t *wfd_sink)
578 GstElement *valve = NULL;
579 GstPad *sinkpad = NULL;
580 GstPad *srcpad = NULL;
581 int result = MM_ERROR_NONE;
583 wfd_sink_debug_fenter();
585 wfd_sink_return_val_if_fail(wfd_sink &&
586 wfd_sink->pipeline &&
587 wfd_sink->pipeline->mainbin &&
588 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst &&
589 wfd_sink->pipeline->mainbin[WFD_SINK_M_A_VALVE].gst,
590 MM_ERROR_WFD_NOT_INITIALIZED);
592 valve = wfd_sink->pipeline->mainbin[WFD_SINK_M_A_VALVE].gst;
593 srcpad = gst_element_get_static_pad(valve, "src");
595 wfd_sink_error("failed to get src pad from %s", GST_ELEMENT_NAME(valve));
599 if (gst_pad_is_linked(srcpad)) {
600 wfd_sink_debug("%s:%s is already linked to %s:%s",
601 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad));
605 result = __mm_wfd_sink_prepare_audio_pipeline(wfd_sink, &sinkpad);
606 if (MM_ERROR_NONE != result) {
607 wfd_sink_error("failed to prepare audio pipeline....");
611 wfd_sink_debug("try to link %s:%s and %s:%s",
612 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
613 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
614 if (GST_PAD_LINK_OK != gst_pad_link_full(srcpad, sinkpad, GST_PAD_LINK_CHECK_NOTHING)) {
615 wfd_sink_error("failed to link %s:%s and %s:%s",
616 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
617 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
621 if (sinkpad != NULL) {
622 gst_object_unref(GST_OBJECT(sinkpad));
627 if (srcpad != NULL) {
628 gst_object_unref(GST_OBJECT(srcpad));
631 /* drop all the audio buffers using valve */
632 g_object_set(G_OBJECT(valve), "drop", FALSE, NULL);
634 wfd_sink_debug_fleave();
636 return MM_ERROR_NONE;
639 if (srcpad != NULL) {
640 gst_object_unref(GST_OBJECT(srcpad));
644 if (sinkpad != NULL) {
645 gst_object_unref(GST_OBJECT(sinkpad));
649 wfd_sink_debug_fleave();
650 return MM_ERROR_WFD_INTERNAL;
653 int __mm_wfd_sink_activate_video_stream(mm_wfd_sink_t *wfd_sink)
655 GstElement *valve = NULL;
656 GstPad *sinkpad = NULL;
657 GstPad *srcpad = NULL;
658 int result = MM_ERROR_NONE;
660 wfd_sink_debug_fenter();
662 wfd_sink_return_val_if_fail(wfd_sink &&
663 wfd_sink->pipeline &&
664 wfd_sink->pipeline->mainbin &&
665 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst &&
666 wfd_sink->pipeline->mainbin[WFD_SINK_M_V_VALVE].gst,
667 MM_ERROR_WFD_NOT_INITIALIZED);
669 valve = wfd_sink->pipeline->mainbin[WFD_SINK_M_V_VALVE].gst;
670 srcpad = gst_element_get_static_pad(valve, "src");
672 wfd_sink_error("failed to get src pad from %s", GST_ELEMENT_NAME(valve));
676 if (gst_pad_is_linked(srcpad)) {
677 wfd_sink_debug("%s is already linked",
678 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad));
682 result = __mm_wfd_sink_prepare_video_pipeline(wfd_sink, &sinkpad);
683 if (MM_ERROR_NONE != result) {
684 wfd_sink_error("failed to prepare video pipeline....");
688 wfd_sink_debug("try to link %s:%s and %s:%s",
689 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
690 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
691 if (GST_PAD_LINK_OK != gst_pad_link_full(srcpad, sinkpad, GST_PAD_LINK_CHECK_NOTHING)) {
692 wfd_sink_error("failed to link %s:%s and %s:%s",
693 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
694 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
697 if (sinkpad != NULL) {
698 gst_object_unref(GST_OBJECT(sinkpad));
703 if (srcpad != NULL) {
704 gst_object_unref(GST_OBJECT(srcpad));
707 /* drop all the video buffers using valve */
708 g_object_set(G_OBJECT(valve), "drop", FALSE, NULL);
710 wfd_sink_debug_fleave();
712 return MM_ERROR_NONE;
715 if (srcpad != NULL) {
716 gst_object_unref(GST_OBJECT(srcpad));
720 if (sinkpad != NULL) {
721 gst_object_unref(GST_OBJECT(sinkpad));
725 wfd_sink_debug_fleave();
726 return MM_ERROR_WFD_INTERNAL;
729 int __mm_wfd_sink_deactivate_audio_stream(mm_wfd_sink_t *wfd_sink, gboolean unprepare)
731 int ret = MM_ERROR_NONE;
733 wfd_sink_debug_fenter();
735 wfd_sink_return_val_if_fail(wfd_sink &&
736 wfd_sink->pipeline &&
737 wfd_sink->pipeline->mainbin &&
738 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst &&
739 wfd_sink->pipeline->mainbin[WFD_SINK_M_A_VALVE].gst,
740 MM_ERROR_WFD_NOT_INITIALIZED);
742 /* drop all the audio buffers using valve */
743 g_object_set(G_OBJECT(wfd_sink->pipeline->mainbin[WFD_SINK_M_A_VALVE].gst), "drop", TRUE, NULL);
746 /* unprepare audio pipeline */
747 ret = __mm_wfd_sink_unprepare_audio_pipeline(wfd_sink);
748 if (ret != MM_ERROR_NONE) {
749 wfd_sink_error("failed to unprepare audio pipeline...");
750 return MM_ERROR_WFD_INTERNAL;
754 wfd_sink_debug_fleave();
756 return MM_ERROR_NONE;
759 int __mm_wfd_sink_deactivate_video_stream(mm_wfd_sink_t *wfd_sink, gboolean unprepare)
761 int ret = MM_ERROR_NONE;
763 wfd_sink_debug_fenter();
765 wfd_sink_return_val_if_fail(wfd_sink &&
766 wfd_sink->pipeline &&
767 wfd_sink->pipeline->mainbin &&
768 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst &&
769 wfd_sink->pipeline->mainbin[WFD_SINK_M_V_VALVE].gst,
770 MM_ERROR_WFD_NOT_INITIALIZED);
772 /* drop all the video buffers using valve */
773 g_object_set(G_OBJECT(wfd_sink->pipeline->mainbin[WFD_SINK_M_V_VALVE].gst), "drop", TRUE, NULL);
776 /* unprepare video pipeline */
777 ret = __mm_wfd_sink_unprepare_video_pipeline(wfd_sink);
778 if (ret != MM_ERROR_NONE) {
779 wfd_sink_error("failed to unprepare video pipeline...");
780 return MM_ERROR_WFD_INTERNAL;
784 wfd_sink_debug_fleave();
786 return MM_ERROR_NONE;
791 _mm_wfd_sink_msg_callback(GstBus *bus, GstMessage *msg, gpointer data)
793 mm_wfd_sink_t *wfd_sink = (mm_wfd_sink_t *) data;
794 const GstStructure *message_structure = gst_message_get_structure(msg);
796 gchar *getname = NULL;
798 wfd_sink_return_val_if_fail(wfd_sink, FALSE);
799 wfd_sink_return_val_if_fail(msg && GST_IS_MESSAGE(msg), FALSE);
801 wfd_sink_debug("got %s(%d) from %s",
802 GST_STR_NULL(GST_MESSAGE_TYPE_NAME(msg)),
803 GST_MESSAGE_TYPE(msg),
804 GST_STR_NULL(GST_OBJECT_NAME(GST_MESSAGE_SRC(msg))));
806 switch (GST_MESSAGE_TYPE(msg)) {
807 case GST_MESSAGE_ERROR: {
808 GError *error = NULL;
812 gst_message_parse_error(msg, &error, &debug);
814 wfd_sink_error("error : %s", error->message);
815 wfd_sink_error("debug : %s", debug);
817 MMWFDSINK_FREEIF(debug);
822 case GST_MESSAGE_WARNING: {
824 GError *error = NULL;
826 gst_message_parse_warning(msg, &error, &debug);
828 wfd_sink_error("warning : %s", error->message);
829 wfd_sink_error("debug : %s", debug);
831 MMWFDSINK_FREEIF(debug);
836 case GST_MESSAGE_STATE_CHANGED: {
837 const GValue *voldstate, *vnewstate, *vpending;
838 GstState oldstate, newstate, pending;
839 const GstStructure *structure;
841 /* get state info from msg */
842 structure = gst_message_get_structure(msg);
843 if (structure == NULL)
846 voldstate = gst_structure_get_value(structure, "old-state");
847 vnewstate = gst_structure_get_value(structure, "new-state");
848 vpending = gst_structure_get_value(structure, "pending-state");
849 if (voldstate == NULL || vnewstate == NULL || vpending == NULL)
852 oldstate = (GstState)voldstate->data[0].v_int;
853 newstate = (GstState)vnewstate->data[0].v_int;
854 pending = (GstState)vpending->data[0].v_int;
856 wfd_sink_debug("state changed [%s] : %s--->%s final : %s",
857 GST_OBJECT_NAME(GST_MESSAGE_SRC(msg)),
858 gst_element_state_get_name((GstState)oldstate),
859 gst_element_state_get_name((GstState)newstate),
860 gst_element_state_get_name((GstState)pending));
862 /* we only handle messages from pipeline */
863 if (msg->src != (GstObject *)wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst)
866 if (oldstate == newstate) {
867 wfd_sink_debug("pipeline reports state transition to old state");
872 case GST_STATE_VOID_PENDING:
874 case GST_STATE_READY:
875 case GST_STATE_PAUSED:
876 case GST_STATE_PLAYING:
883 case GST_MESSAGE_CLOCK_LOST: {
884 GstClock *clock = NULL;
885 gst_message_parse_clock_lost(msg, &clock);
886 wfd_sink_debug("The current clock[%s] as selected by the pipeline became unusable.",
887 (clock ? GST_OBJECT_NAME(clock) : "NULL"));
891 case GST_MESSAGE_NEW_CLOCK: {
892 GstClock *clock = NULL;
893 gst_message_parse_new_clock(msg, &clock);
897 if (wfd_sink->clock) {
898 if (wfd_sink->clock != clock)
899 wfd_sink_debug("clock is changed! [%s] -->[%s]",
900 GST_STR_NULL(GST_OBJECT_NAME(wfd_sink->clock)),
901 GST_STR_NULL(GST_OBJECT_NAME(clock)));
903 wfd_sink_debug("same clock is selected again! [%s]",
904 GST_STR_NULL(GST_OBJECT_NAME(clock)));
906 wfd_sink_debug("new clock [%s] was selected in the pipeline",
907 (GST_STR_NULL(GST_OBJECT_NAME(clock))));
910 wfd_sink->clock = clock;
914 case GST_MESSAGE_APPLICATION: {
915 const gchar *message_structure_name;
917 message_structure_name = gst_structure_get_name(message_structure);
918 if (!message_structure_name)
921 wfd_sink_debug("message name : %s", GST_STR_NULL(message_structure_name));
925 case GST_MESSAGE_ELEMENT: {
926 const gchar *structure_name = NULL;
928 structure_name = gst_structure_get_name(message_structure);
929 if (structure_name) {
930 wfd_sink_debug("got element specific message[%s]", GST_STR_NULL(structure_name));
931 if (g_strrstr(structure_name, "GstUDPSrcTimeout")) {
932 wfd_sink_error("Got %s, post error message", GST_STR_NULL(structure_name));
933 MMWFDSINK_POST_MESSAGE(wfd_sink,
934 MM_ERROR_WFD_INTERNAL,
935 MMWFDSINK_CURRENT_STATE(wfd_sink));
936 } else if (g_strrstr(structure_name, "GstWFDSessionTimeout")) {
937 wfd_sink_error("Got %s, post error message", GST_STR_NULL(structure_name));
938 MMWFDSINK_POST_MESSAGE(wfd_sink,
939 MM_ERROR_WFD_INTERNAL,
940 MM_WFD_SINK_STATE_DISCONNECTED);
946 case GST_MESSAGE_PROGRESS: {
947 GstProgressType type = GST_PROGRESS_TYPE_ERROR;
948 gchar *category = NULL, *text = NULL;
950 gst_message_parse_progress(msg, &type, &category, &text);
951 wfd_sink_debug("%s : %s ", GST_STR_NULL(category), GST_STR_NULL(text));
954 case GST_PROGRESS_TYPE_START:
956 case GST_PROGRESS_TYPE_COMPLETE:
957 if (category && !strcmp(category, "open")) {
958 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_CONNECTED);
959 } else if (category && !strcmp(category, "play")) {
960 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_PLAYING);
961 /*_mm_wfd_sink_correct_pipeline_latency (wfd_sink); */
962 } else if (category && !strcmp(category, "pause")) {
963 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_PAUSED);
964 } else if (category && !strcmp(category, "close")) {
965 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_DISCONNECTED);
968 case GST_PROGRESS_TYPE_CANCELED:
970 case GST_PROGRESS_TYPE_ERROR:
971 if (category && !strcmp(category, "open")) {
972 wfd_sink_error("got error : %s", GST_STR_NULL(text));
973 /*_mm_wfd_sink_disconnect (wfd_sink); */
974 MMWFDSINK_POST_MESSAGE(wfd_sink,
975 MM_ERROR_WFD_INTERNAL,
976 MMWFDSINK_CURRENT_STATE(wfd_sink));
977 } else if (category && !strcmp(category, "play")) {
978 wfd_sink_error("got error : %s", GST_STR_NULL(text));
979 /*_mm_wfd_sink_disconnect (wfd_sink); */
980 MMWFDSINK_POST_MESSAGE(wfd_sink,
981 MM_ERROR_WFD_INTERNAL,
982 MMWFDSINK_CURRENT_STATE(wfd_sink));
983 } else if (category && !strcmp(category, "pause")) {
984 wfd_sink_error("got error : %s", GST_STR_NULL(text));
985 /*_mm_wfd_sink_disconnect (wfd_sink); */
986 MMWFDSINK_POST_MESSAGE(wfd_sink,
987 MM_ERROR_WFD_INTERNAL,
988 MMWFDSINK_CURRENT_STATE(wfd_sink));
989 } else if (category && !strcmp(category, "close")) {
990 wfd_sink_error("got error : %s", GST_STR_NULL(text));
991 /*_mm_wfd_sink_disconnect (wfd_sink); */
992 MMWFDSINK_POST_MESSAGE(wfd_sink,
993 MM_ERROR_WFD_INTERNAL,
994 MMWFDSINK_CURRENT_STATE(wfd_sink));
996 wfd_sink_error("got error : %s", GST_STR_NULL(text));
1000 wfd_sink_error("progress message has no type");
1004 MMWFDSINK_FREEIF(category);
1005 MMWFDSINK_FREEIF(text);
1009 case GST_MESSAGE_ASYNC_START:
1010 getname = gst_element_get_name(GST_MESSAGE_SRC(msg));
1011 wfd_sink_debug("GST_MESSAGE_ASYNC_START : %s", getname);
1012 MMWFDSINK_FREEIF(getname);
1014 case GST_MESSAGE_ASYNC_DONE:
1015 getname = gst_element_get_name(GST_MESSAGE_SRC(msg));
1016 wfd_sink_debug("GST_MESSAGE_ASYNC_DONE : %s", getname);
1017 MMWFDSINK_FREEIF(getname);
1019 case GST_MESSAGE_UNKNOWN:
1020 case GST_MESSAGE_INFO:
1021 case GST_MESSAGE_TAG:
1022 case GST_MESSAGE_BUFFERING:
1023 case GST_MESSAGE_EOS:
1024 case GST_MESSAGE_STATE_DIRTY:
1025 case GST_MESSAGE_STEP_DONE:
1026 case GST_MESSAGE_CLOCK_PROVIDE:
1027 case GST_MESSAGE_STRUCTURE_CHANGE:
1028 case GST_MESSAGE_STREAM_STATUS:
1029 case GST_MESSAGE_SEGMENT_START:
1030 case GST_MESSAGE_SEGMENT_DONE:
1031 case GST_MESSAGE_DURATION:
1032 case GST_MESSAGE_LATENCY:
1033 case GST_MESSAGE_REQUEST_STATE:
1034 case GST_MESSAGE_STEP_START:
1035 case GST_MESSAGE_QOS:
1036 case GST_MESSAGE_ANY:
1039 wfd_sink_debug("unhandled message");
1047 __mm_wfd_sink_gst_element_add_bucket_to_bin(GstBin *bin, GList *element_bucket, gboolean need_prepare)
1049 GList *bucket = element_bucket;
1050 MMWFDSinkGstElement *element = NULL;
1051 int successful_add_count = 0;
1053 wfd_sink_debug_fenter();
1055 wfd_sink_return_val_if_fail(element_bucket, 0);
1056 wfd_sink_return_val_if_fail(bin, 0);
1058 for (; bucket; bucket = bucket->next) {
1059 element = (MMWFDSinkGstElement *)bucket->data;
1061 if (element && element->gst) {
1063 gst_element_set_state(GST_ELEMENT(element->gst), GST_STATE_READY);
1065 if (!gst_bin_add(GST_BIN(bin), GST_ELEMENT(element->gst))) {
1066 wfd_sink_error("failed to add element [%s] to bin [%s]",
1067 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT(element->gst))),
1068 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT_CAST(bin))));
1072 wfd_sink_debug("add element [%s] to bin [%s]",
1073 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT(element->gst))),
1074 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT_CAST(bin))));
1076 successful_add_count++;
1080 wfd_sink_debug_fleave();
1082 return successful_add_count;
1086 __mm_wfd_sink_gst_element_link_bucket(GList *element_bucket)
1088 GList *bucket = element_bucket;
1089 MMWFDSinkGstElement *element = NULL;
1090 MMWFDSinkGstElement *prv_element = NULL;
1091 gint successful_link_count = 0;
1093 wfd_sink_debug_fenter();
1095 wfd_sink_return_val_if_fail(element_bucket, -1);
1097 prv_element = (MMWFDSinkGstElement *)bucket->data;
1098 bucket = bucket->next;
1100 for (; bucket; bucket = bucket->next) {
1101 element = (MMWFDSinkGstElement *)bucket->data;
1103 if (element && element->gst) {
1104 if (gst_element_link(GST_ELEMENT(prv_element->gst), GST_ELEMENT(element->gst))) {
1105 wfd_sink_debug("linking [%s] to [%s] success",
1106 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT(prv_element->gst))),
1107 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT(element->gst))));
1108 successful_link_count++;
1110 wfd_sink_error("linking [%s] to [%s] failed",
1111 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT(prv_element->gst))),
1112 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT(element->gst))));
1117 prv_element = element;
1120 wfd_sink_debug_fleave();
1122 return successful_link_count;
1126 __mm_wfd_sink_check_state(mm_wfd_sink_t *wfd_sink, MMWFDSinkCommandType cmd)
1128 MMWFDSinkStateType cur_state = MM_WFD_SINK_STATE_NONE;
1130 wfd_sink_debug_fenter();
1132 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
1134 MMWFDSINK_PRINT_STATE(wfd_sink);
1136 cur_state = MMWFDSINK_CURRENT_STATE(wfd_sink);
1139 case MM_WFD_SINK_COMMAND_CREATE:
1140 if (cur_state != MM_WFD_SINK_STATE_NONE)
1143 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_NULL;
1146 case MM_WFD_SINK_COMMAND_PREPARE:
1147 if (cur_state == MM_WFD_SINK_STATE_PREPARED)
1149 else if (cur_state != MM_WFD_SINK_STATE_NULL)
1152 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_PREPARED;
1155 case MM_WFD_SINK_COMMAND_CONNECT:
1156 if (cur_state == MM_WFD_SINK_STATE_CONNECTED)
1158 else if (cur_state != MM_WFD_SINK_STATE_PREPARED)
1161 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_CONNECTED;
1164 case MM_WFD_SINK_COMMAND_START:
1165 if (cur_state == MM_WFD_SINK_STATE_PLAYING)
1167 else if (cur_state != MM_WFD_SINK_STATE_CONNECTED)
1170 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_PLAYING;
1173 case MM_WFD_SINK_COMMAND_PAUSE:
1174 if (cur_state == MM_WFD_SINK_STATE_PAUSED)
1176 else if (cur_state != MM_WFD_SINK_STATE_PLAYING)
1179 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_PAUSED;
1182 case MM_WFD_SINK_COMMAND_RESUME:
1183 if (cur_state == MM_WFD_SINK_STATE_PLAYING)
1185 else if (cur_state != MM_WFD_SINK_STATE_PAUSED)
1188 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_PLAYING;
1191 case MM_WFD_SINK_COMMAND_DISCONNECT:
1192 if (cur_state == MM_WFD_SINK_STATE_NONE ||
1193 cur_state == MM_WFD_SINK_STATE_NULL ||
1194 cur_state == MM_WFD_SINK_STATE_PREPARED ||
1195 cur_state == MM_WFD_SINK_STATE_DISCONNECTED)
1197 else if (cur_state != MM_WFD_SINK_STATE_PLAYING &&
1198 cur_state != MM_WFD_SINK_STATE_CONNECTED &&
1199 cur_state != MM_WFD_SINK_STATE_PAUSED)
1202 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_DISCONNECTED;
1205 case MM_WFD_SINK_COMMAND_UNPREPARE:
1206 if (cur_state == MM_WFD_SINK_STATE_NONE ||
1207 cur_state == MM_WFD_SINK_STATE_NULL)
1210 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_NULL;
1213 case MM_WFD_SINK_COMMAND_DESTROY:
1214 if (cur_state == MM_WFD_SINK_STATE_NONE)
1217 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_NONE;
1224 wfd_sink->cmd = cmd;
1226 wfd_sink_debug_fleave();
1228 return MM_ERROR_NONE;
1231 wfd_sink_debug("already %s state, nothing to do.", MMWFDSINK_STATE_GET_NAME(cur_state));
1232 return MM_ERROR_WFD_NO_OP;
1236 wfd_sink_error("current state is invalid.", MMWFDSINK_STATE_GET_NAME(cur_state));
1237 return MM_ERROR_WFD_INVALID_STATE;
1240 static int __mm_wfd_sink_set_state(mm_wfd_sink_t *wfd_sink, MMWFDSinkStateType state)
1242 wfd_sink_debug_fenter();
1244 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
1246 if (MMWFDSINK_CURRENT_STATE(wfd_sink) == state) {
1247 wfd_sink_error("already state(%s)", MMWFDSINK_STATE_GET_NAME(state));
1248 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_NONE;
1249 return MM_ERROR_NONE;
1252 /* update wi-fi display state */
1253 MMWFDSINK_PREVIOUS_STATE(wfd_sink) = MMWFDSINK_CURRENT_STATE(wfd_sink);
1254 MMWFDSINK_CURRENT_STATE(wfd_sink) = state;
1256 if (MMWFDSINK_CURRENT_STATE(wfd_sink) == MMWFDSINK_PENDING_STATE(wfd_sink))
1257 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_NONE;
1259 /* post state message to application */
1260 MMWFDSINK_POST_MESSAGE(wfd_sink,
1262 MMWFDSINK_CURRENT_STATE(wfd_sink));
1265 MMWFDSINK_PRINT_STATE(wfd_sink);
1267 wfd_sink_debug_fleave();
1269 return MM_ERROR_NONE;
1273 __mm_wfd_sink_set_pipeline_state(mm_wfd_sink_t *wfd_sink, GstState state, gboolean async)
1275 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
1276 GstState cur_state = GST_STATE_VOID_PENDING;
1277 GstState pending_state = GST_STATE_VOID_PENDING;
1279 wfd_sink_debug_fenter();
1281 wfd_sink_return_val_if_fail(wfd_sink &&
1282 wfd_sink->pipeline &&
1283 wfd_sink->pipeline->mainbin &&
1284 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst,
1285 MM_ERROR_WFD_NOT_INITIALIZED);
1287 wfd_sink_return_val_if_fail(state > GST_STATE_VOID_PENDING,
1288 MM_ERROR_WFD_INVALID_ARGUMENT);
1290 wfd_sink_debug("try to set %s state ", gst_element_state_get_name(state));
1292 result = gst_element_set_state(wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst, state);
1293 if (result == GST_STATE_CHANGE_FAILURE) {
1294 wfd_sink_error("fail to set %s state....", gst_element_state_get_name(state));
1295 return MM_ERROR_WFD_INTERNAL;
1299 wfd_sink_debug("wait for changing state is completed ");
1301 result = gst_element_get_state(wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst,
1302 &cur_state, &pending_state, wfd_sink->ini.state_change_timeout * GST_SECOND);
1303 if (result == GST_STATE_CHANGE_FAILURE) {
1304 wfd_sink_error("fail to get state within %d seconds....", wfd_sink->ini.state_change_timeout);
1306 __mm_wfd_sink_dump_pipeline_state(wfd_sink);
1308 return MM_ERROR_WFD_INTERNAL;
1309 } else if (result == GST_STATE_CHANGE_NO_PREROLL) {
1310 wfd_sink_debug("successfully changed state but is not able to provide data yet");
1313 wfd_sink_debug("cur state is %s, pending state is %s",
1314 gst_element_state_get_name(cur_state),
1315 gst_element_state_get_name(pending_state));
1319 wfd_sink_debug_fleave();
1321 return MM_ERROR_NONE;
1325 _mm_wfd_sink_reset_basetime(mm_wfd_sink_t *wfd_sink)
1327 GstClockTime base_time = GST_CLOCK_TIME_NONE;
1330 wfd_sink_debug_fenter();
1332 wfd_sink_return_if_fail(wfd_sink &&
1333 wfd_sink->pipeline &&
1334 wfd_sink->pipeline->mainbin &&
1335 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst);
1336 wfd_sink_return_if_fail(wfd_sink->need_to_reset_basetime);
1339 if (wfd_sink->clock)
1340 base_time = gst_clock_get_time(wfd_sink->clock);
1342 if (GST_CLOCK_TIME_IS_VALID(base_time)) {
1344 wfd_sink_debug("set pipeline base_time as now [%"GST_TIME_FORMAT"]", GST_TIME_ARGS(base_time));
1346 for (i = 0; i < WFD_SINK_M_NUM; i++) {
1347 if (wfd_sink->pipeline->mainbin[i].gst)
1348 gst_element_set_base_time(GST_ELEMENT_CAST(wfd_sink->pipeline->mainbin[i].gst), base_time);
1351 if (wfd_sink->pipeline->v_decodebin) {
1352 for (i = 0; i < WFD_SINK_V_D_NUM; i++) {
1353 if (wfd_sink->pipeline->v_decodebin[i].gst)
1354 gst_element_set_base_time(GST_ELEMENT_CAST(wfd_sink->pipeline->v_decodebin[i].gst), base_time);
1358 if (wfd_sink->pipeline->v_sinkbin) {
1359 for (i = 0; i < WFD_SINK_V_S_NUM; i++) {
1360 if (wfd_sink->pipeline->v_sinkbin[i].gst)
1361 gst_element_set_base_time(GST_ELEMENT_CAST(wfd_sink->pipeline->v_sinkbin[i].gst), base_time);
1365 if (wfd_sink->pipeline->a_decodebin) {
1366 for (i = 0; i < WFD_SINK_A_D_NUM; i++) {
1367 if (wfd_sink->pipeline->a_decodebin[i].gst)
1368 gst_element_set_base_time(GST_ELEMENT_CAST(wfd_sink->pipeline->a_decodebin[i].gst), base_time);
1372 if (wfd_sink->pipeline->a_sinkbin) {
1373 for (i = 0; i < WFD_SINK_A_S_NUM; i++) {
1374 if (wfd_sink->pipeline->a_sinkbin[i].gst)
1375 gst_element_set_base_time(GST_ELEMENT_CAST(wfd_sink->pipeline->a_sinkbin[i].gst), base_time);
1379 wfd_sink->need_to_reset_basetime = FALSE;
1382 wfd_sink_debug_fleave();
1388 __mm_wfd_sink_unprepare_video_pipeline(mm_wfd_sink_t *wfd_sink)
1390 GstElement *pipeline = NULL;
1391 GstElement *v_decodebin = NULL;
1392 GstElement *v_sinkbin = NULL;
1393 GstPad *sinkpad = NULL;
1394 GstPad *srcpad = NULL;
1395 int ret = MM_ERROR_NONE;
1397 wfd_sink_debug_fenter();
1399 wfd_sink_return_val_if_fail(wfd_sink &&
1401 MM_ERROR_WFD_NOT_INITIALIZED);
1403 PRINT_WFD_REF_COUNT(wfd_sink);
1404 wfd_sink_error("No-error:unprepare video sink bin");
1405 if (wfd_sink->pipeline->v_sinkbin && wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_BIN].gst) {
1406 v_sinkbin = wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_BIN].gst;
1408 if ((pipeline = GST_ELEMENT_CAST(gst_element_get_parent(v_sinkbin)))) {
1409 sinkpad = gst_element_get_static_pad(v_sinkbin, "sink");
1411 wfd_sink_error("failed to get sink pad from %s", GST_ELEMENT_NAME(v_sinkbin));
1415 if (gst_pad_is_linked(sinkpad)) {
1416 srcpad = gst_pad_get_peer(sinkpad);
1418 wfd_sink_error("failed to get peer pad of %s:%s",
1419 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1423 wfd_sink_debug("try to unlink %s:%s and %s:%s",
1424 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1425 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1426 if (!gst_pad_unlink(srcpad, sinkpad)) {
1427 wfd_sink_error("failed to unlink %s:%s and %s:%s",
1428 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1429 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1432 gst_object_unref(srcpad);
1435 wfd_sink_debug("video sinkbin's sinkpad is not linked, no need to unlink it");
1437 gst_object_unref(sinkpad);
1440 gst_object_ref(v_sinkbin);
1441 if (!gst_bin_remove(GST_BIN(pipeline), GST_ELEMENT(v_sinkbin))) {
1442 wfd_sink_error("failed to remove %s from %s",
1443 GST_ELEMENT_NAME(v_sinkbin), GST_ELEMENT_NAME(pipeline));
1447 gst_object_unref(pipeline);
1451 ret = __mm_wfd_sink_destroy_video_sinkbin(wfd_sink);
1452 if (ret != MM_ERROR_NONE) {
1453 wfd_sink_error("failed to destroy video sinkbin");
1457 PRINT_WFD_REF_COUNT(wfd_sink);
1459 wfd_sink_error("No-error:unprepare video decode bin");
1460 if (wfd_sink->pipeline->v_decodebin && wfd_sink->pipeline->v_decodebin[WFD_SINK_V_D_BIN].gst) {
1461 v_decodebin = wfd_sink->pipeline->v_decodebin[WFD_SINK_V_D_BIN].gst;
1463 if ((pipeline = GST_ELEMENT_CAST(gst_element_get_parent(v_decodebin)))) {
1464 sinkpad = gst_element_get_static_pad(v_decodebin, "sink");
1466 wfd_sink_error("failed to get sink pad from %s", GST_ELEMENT_NAME(v_decodebin));
1470 if (gst_pad_is_linked(sinkpad)) {
1471 srcpad = gst_pad_get_peer(sinkpad);
1473 wfd_sink_error("failed to get peer pad of %s:%s",
1474 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1478 wfd_sink_debug("try to unlink %s:%s and %s:%s",
1479 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1480 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1481 if (!gst_pad_unlink(srcpad, sinkpad)) {
1482 wfd_sink_error("failed to unlink %s:%s and %s:%s",
1483 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1484 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1487 gst_object_unref(srcpad);
1490 wfd_sink_debug("video decodebin's sinkpad is not linked, no need to unlink it");
1492 gst_object_unref(sinkpad);
1495 srcpad = gst_element_get_static_pad(v_decodebin, "src");
1497 wfd_sink_error("failed to get src pad from %s", GST_ELEMENT_NAME(v_decodebin));
1501 if (gst_pad_is_linked(srcpad)) {
1502 sinkpad = gst_pad_get_peer(srcpad);
1504 wfd_sink_error("failed to get peer pad of %s:%s",
1505 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad));
1509 wfd_sink_debug("try to unlink %s:%s and %s:%s",
1510 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1511 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1512 if (!gst_pad_unlink(srcpad, sinkpad)) {
1513 wfd_sink_error("failed to unlink %s:%s and %s:%s",
1514 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1515 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1518 gst_object_unref(sinkpad);
1521 wfd_sink_debug("video decodebin's srcpad is not linked, no need to unlink it");
1523 gst_object_unref(srcpad);
1526 wfd_sink_error("try to remove %s from %s",
1527 GST_ELEMENT_NAME(v_decodebin), GST_ELEMENT_NAME(pipeline));
1528 gst_object_ref(v_decodebin);
1529 if (!gst_bin_remove(GST_BIN(pipeline), GST_ELEMENT(v_decodebin))) {
1530 wfd_sink_error("failed to remove %s from %s",
1531 GST_ELEMENT_NAME(v_decodebin), GST_ELEMENT_NAME(pipeline));
1535 gst_object_unref(pipeline);
1539 ret = __mm_wfd_sink_destroy_video_decodebin(wfd_sink);
1540 if (ret != MM_ERROR_NONE) {
1541 wfd_sink_error("failed to destroy video decodebin");
1545 PRINT_WFD_REF_COUNT(wfd_sink);
1547 wfd_sink_debug_fleave();
1554 gst_object_unref(pipeline);
1559 gst_object_unref(sinkpad);
1564 gst_object_unref(srcpad);
1568 /* need to notify to app */
1569 MMWFDSINK_POST_MESSAGE(wfd_sink,
1570 MM_ERROR_WFD_INTERNAL,
1571 MMWFDSINK_CURRENT_STATE(wfd_sink));
1573 return MM_ERROR_WFD_INTERNAL;
1577 __mm_wfd_sink_prepare_video_pipeline(mm_wfd_sink_t *wfd_sink, GstPad **pad)
1579 GstElement *pipeline = NULL;
1580 GstElement *v_decodebin = NULL;
1581 GstElement *v_sinkbin = NULL;
1582 GstPad *srcpad = NULL;
1583 GstPad *sinkpad = NULL;
1585 wfd_sink_debug_fenter();
1587 wfd_sink_return_val_if_fail(wfd_sink &&
1588 wfd_sink->pipeline &&
1589 wfd_sink->pipeline->mainbin &&
1590 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst,
1591 MM_ERROR_WFD_NOT_INITIALIZED);
1593 /* check video decodebin is linked */
1594 if (!wfd_sink->video_decodebin_is_linked) {
1595 /* check video decodebin is created */
1596 if (wfd_sink->pipeline->v_decodebin == NULL) {
1597 if (MM_ERROR_NONE != __mm_wfd_sink_create_video_decodebin(wfd_sink)) {
1598 wfd_sink_error("failed to create video decodebin....");
1603 if (MM_ERROR_NONE != __mm_wfd_sink_link_video_decodebin(wfd_sink)) {
1604 wfd_sink_error("failed to link video decodebin.....");
1609 /* check video sinkbin is created */
1610 if (wfd_sink->pipeline->v_sinkbin == NULL) {
1611 if (MM_ERROR_NONE != __mm_wfd_sink_create_video_sinkbin(wfd_sink)) {
1612 wfd_sink_error("failed to create video sinkbin....");
1616 /* add video decodebin to pipeline */
1617 if (wfd_sink->pipeline->v_decodebin && wfd_sink->pipeline->v_decodebin[WFD_SINK_V_D_BIN].gst) {
1618 v_decodebin = wfd_sink->pipeline->v_decodebin[WFD_SINK_V_D_BIN].gst;
1620 pipeline = GST_ELEMENT_CAST(gst_element_get_parent(v_decodebin));
1622 pipeline = wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst;
1624 if (GST_STATE(v_decodebin) <= GST_STATE_NULL) {
1625 wfd_sink_debug("need to prepare %s", GST_ELEMENT_NAME(v_decodebin));
1626 if (GST_STATE_CHANGE_FAILURE == gst_element_set_state(v_decodebin, GST_STATE_READY)) {
1627 wfd_sink_error("failed to set state(READY) to video decodebin");
1632 wfd_sink_debug("try to add %s to %s",
1633 GST_ELEMENT_NAME(v_decodebin), GST_ELEMENT_NAME(pipeline));
1634 if (!gst_bin_add(GST_BIN(pipeline), GST_ELEMENT(v_decodebin))) {
1635 wfd_sink_error("failed to add %s to %s",
1636 GST_ELEMENT_NAME(v_decodebin), GST_ELEMENT_NAME(pipeline));
1640 wfd_sink_debug("need to sync state %s with its parent", GST_ELEMENT_NAME(v_decodebin));
1641 if (!gst_element_sync_state_with_parent(GST_ELEMENT(v_decodebin))) {
1642 wfd_sink_error("failed to sync %s state with parent", GST_ELEMENT_NAME(v_decodebin));
1646 wfd_sink_debug("%s is already added to %s",
1647 GST_ELEMENT_NAME(v_decodebin), GST_ELEMENT_NAME(pipeline));
1648 gst_object_unref(pipeline);
1652 wfd_sink_warning("going on without video decodebin....");
1655 /* add video sinkbin to pipeline */
1656 if (wfd_sink->pipeline->v_sinkbin && wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_BIN].gst) {
1657 v_sinkbin = wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_BIN].gst;
1659 pipeline = GST_ELEMENT_CAST(gst_element_get_parent(v_sinkbin));
1661 pipeline = wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst;
1663 /* prepare video sinkbin before adding */
1664 if (GST_STATE(v_sinkbin) <= GST_STATE_NULL) {
1665 wfd_sink_debug("need to prepare %s", GST_ELEMENT_NAME(v_sinkbin));
1666 if (GST_STATE_CHANGE_FAILURE == gst_element_set_state(v_sinkbin, GST_STATE_READY)) {
1667 wfd_sink_error("failed to set state(READY) to video sinkbin");
1671 /* add video sinkbin to pipeline */
1672 wfd_sink_debug("try to add %s to %s",
1673 GST_ELEMENT_NAME(v_sinkbin), GST_ELEMENT_NAME(pipeline));
1674 if (!gst_bin_add(GST_BIN(pipeline), GST_ELEMENT(v_sinkbin))) {
1675 wfd_sink_error("failed to add %s to %s",
1676 GST_ELEMENT_NAME(v_sinkbin), GST_ELEMENT_NAME(pipeline));
1680 /* sync state with parent */
1681 wfd_sink_debug("need to sync state %s with its parent", GST_ELEMENT_NAME(v_sinkbin));
1682 if (!gst_element_sync_state_with_parent(GST_ELEMENT(v_sinkbin))) {
1683 wfd_sink_error("failed to sync %s state with parent", GST_ELEMENT_NAME(v_sinkbin));
1687 wfd_sink_debug("%s is already added to %s",
1688 GST_ELEMENT_NAME(v_sinkbin), GST_ELEMENT_NAME(pipeline));
1689 gst_object_unref(pipeline);
1693 wfd_sink_warning("going on without video sinkbin....");
1697 /* link video decodebin and sinkbin */
1698 if (wfd_sink->pipeline->v_decodebin && wfd_sink->pipeline->v_decodebin[WFD_SINK_V_D_BIN].gst) {
1699 v_decodebin = wfd_sink->pipeline->v_decodebin[WFD_SINK_V_D_BIN].gst;
1702 *pad = gst_element_get_static_pad(v_decodebin, "sink");
1704 if (wfd_sink->pipeline->v_sinkbin && wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_BIN].gst) {
1706 v_sinkbin = wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_BIN].gst;
1708 srcpad = gst_element_get_static_pad(v_decodebin, "src");
1710 wfd_sink_error("faied to get srcpad from %s", GST_ELEMENT_NAME(v_decodebin));
1714 if (!gst_pad_is_linked(srcpad)) {
1715 sinkpad = gst_element_get_static_pad(v_sinkbin, "sink");
1717 wfd_sink_error("faied to get sinkpad from %s", GST_ELEMENT_NAME(v_sinkbin));
1721 wfd_sink_debug("try to link %s:%s and %s:%s",
1722 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1723 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1724 if (GST_PAD_LINK_OK != gst_pad_link_full(srcpad, sinkpad, GST_PAD_LINK_CHECK_NOTHING)) {
1725 wfd_sink_error("failed to link %s:%s and %s:%s",
1726 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1727 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1730 gst_object_unref(sinkpad);
1733 gst_object_unref(srcpad);
1736 } else if (wfd_sink->pipeline->v_sinkbin && wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_BIN].gst) {
1737 v_sinkbin = wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_BIN].gst;
1739 *pad = gst_element_get_static_pad(v_sinkbin, "sink");
1742 wfd_sink_debug_fleave();
1744 return MM_ERROR_NONE;
1748 if (sinkpad != NULL) {
1749 gst_object_unref(sinkpad);
1753 if (srcpad != NULL) {
1754 gst_object_unref(srcpad);
1758 /* need to notify to app */
1759 MMWFDSINK_POST_MESSAGE(wfd_sink,
1760 MM_ERROR_WFD_INTERNAL,
1761 MMWFDSINK_CURRENT_STATE(wfd_sink));
1763 return MM_ERROR_WFD_INTERNAL;
1767 __mm_wfd_sink_unprepare_audio_pipeline(mm_wfd_sink_t *wfd_sink)
1769 GstElement *pipeline = NULL;
1770 GstElement *a_decodebin = NULL;
1771 GstElement *a_sinkbin = NULL;
1772 GstPad *sinkpad = NULL;
1773 GstPad *srcpad = NULL;
1774 int ret = MM_ERROR_NONE;
1776 wfd_sink_debug_fenter();
1778 wfd_sink_return_val_if_fail(wfd_sink &&
1780 MM_ERROR_WFD_NOT_INITIALIZED);
1782 if (wfd_sink->stream_info.audio_stream_info.codec == MM_WFD_SINK_AUDIO_CODEC_NONE) {
1783 wfd_sink_debug("Skip unprepare audio pipeline for none audio codec.");
1784 wfd_sink_debug_fleave();
1785 return MM_ERROR_NONE;
1788 wfd_sink_error("No-error:unprepare audio sink bin");
1789 PRINT_WFD_REF_COUNT(wfd_sink);
1791 if (wfd_sink->pipeline->a_sinkbin && wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_BIN].gst) {
1792 a_sinkbin = wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_BIN].gst;
1794 if ((pipeline = GST_ELEMENT_CAST(gst_element_get_parent(a_sinkbin)))) {
1795 sinkpad = gst_element_get_static_pad(a_sinkbin, "sink");
1797 wfd_sink_error("failed to get sink pad from %s", GST_ELEMENT_NAME(a_sinkbin));
1801 if (gst_pad_is_linked(sinkpad)) {
1802 srcpad = gst_pad_get_peer(sinkpad);
1804 wfd_sink_error("failed to get peer pad of %s:%s",
1805 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1809 wfd_sink_debug("try to unlink %s:%s and %s:%s",
1810 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1811 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1812 if (!gst_pad_unlink(srcpad, sinkpad)) {
1813 wfd_sink_error("failed to unlink %s:%s and %s:%s",
1814 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1815 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1818 gst_object_unref(srcpad);
1821 wfd_sink_debug("audio sinkbin's sinkpad is not linked, no need to unlink it");
1823 gst_object_unref(sinkpad);
1826 gst_object_ref(a_sinkbin);
1827 if (!gst_bin_remove(GST_BIN(pipeline), GST_ELEMENT(a_sinkbin))) {
1828 wfd_sink_error("failed to remove %s from %s",
1829 GST_ELEMENT_NAME(a_sinkbin), GST_ELEMENT_NAME(pipeline));
1833 gst_object_unref(pipeline);
1837 ret = __mm_wfd_sink_destroy_audio_sinkbin(wfd_sink);
1838 if (ret != MM_ERROR_NONE) {
1839 wfd_sink_error("failed to destroy audio sinkbin");
1843 PRINT_WFD_REF_COUNT(wfd_sink);
1845 wfd_sink_error("No-error:unprepare audio decode bin");
1846 if (wfd_sink->pipeline->a_decodebin && wfd_sink->pipeline->a_decodebin[WFD_SINK_A_D_BIN].gst) {
1847 a_decodebin = wfd_sink->pipeline->a_decodebin[WFD_SINK_A_D_BIN].gst;
1849 if ((pipeline = GST_ELEMENT_CAST(gst_element_get_parent(a_decodebin)))) {
1850 sinkpad = gst_element_get_static_pad(a_decodebin, "sink");
1852 wfd_sink_error("failed to get sink pad from %s", GST_ELEMENT_NAME(a_decodebin));
1856 if (gst_pad_is_linked(sinkpad)) {
1857 srcpad = gst_pad_get_peer(sinkpad);
1859 wfd_sink_error("failed to get peer pad of %s:%s",
1860 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1864 wfd_sink_debug("try to unlink %s:%s and %s:%s",
1865 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1866 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1867 if (!gst_pad_unlink(srcpad, sinkpad)) {
1868 wfd_sink_error("failed to unlink %s:%s and %s:%s",
1869 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1870 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1873 gst_object_unref(srcpad);
1876 wfd_sink_debug("audio decodebin's sinkpad is not linked, no need to unlink it");
1878 gst_object_unref(sinkpad);
1881 srcpad = gst_element_get_static_pad(a_decodebin, "src");
1883 wfd_sink_error("failed to get src pad from %s", GST_ELEMENT_NAME(a_decodebin));
1887 if (gst_pad_is_linked(srcpad)) {
1888 sinkpad = gst_pad_get_peer(srcpad);
1890 wfd_sink_error("failed to get peer pad of %s:%s",
1891 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad));
1892 return MM_ERROR_WFD_INTERNAL;
1895 wfd_sink_debug("try to unlink %s:%s and %s:%s",
1896 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1897 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1898 if (!gst_pad_unlink(srcpad, sinkpad)) {
1899 wfd_sink_error("failed to unlink %s:%s and %s:%s",
1900 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1901 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1902 return MM_ERROR_WFD_INTERNAL;
1904 gst_object_unref(sinkpad);
1907 wfd_sink_debug("audio decodebin's srcpad is not linked, no need to unlink it");
1909 gst_object_unref(srcpad);
1912 wfd_sink_error("try to remove %s from %s",
1913 GST_ELEMENT_NAME(a_decodebin), GST_ELEMENT_NAME(pipeline));
1914 gst_object_ref(a_decodebin);
1915 if (!gst_bin_remove(GST_BIN(pipeline), GST_ELEMENT(a_decodebin))) {
1916 wfd_sink_error("failed to remove %s from %s",
1917 GST_ELEMENT_NAME(a_decodebin), GST_ELEMENT_NAME(pipeline));
1921 gst_object_unref(pipeline);
1925 ret = __mm_wfd_sink_destroy_audio_decodebin(wfd_sink);
1926 if (ret != MM_ERROR_NONE) {
1927 wfd_sink_error("failed to destroy audio decodebin");
1931 PRINT_WFD_REF_COUNT(wfd_sink);
1933 wfd_sink_debug_fleave();
1940 gst_object_unref(pipeline);
1944 gst_object_unref(sinkpad);
1949 gst_object_unref(srcpad);
1953 /* need to notify to app */
1954 MMWFDSINK_POST_MESSAGE(wfd_sink,
1955 MM_ERROR_WFD_INTERNAL,
1956 MMWFDSINK_CURRENT_STATE(wfd_sink));
1958 return MM_ERROR_WFD_INTERNAL;
1962 __mm_wfd_sink_prepare_audio_pipeline(mm_wfd_sink_t *wfd_sink, GstPad **pad)
1964 GstElement *pipeline = NULL;
1965 GstElement *a_decodebin = NULL;
1966 GstElement *a_sinkbin = NULL;
1967 GstPad *srcpad = NULL;
1968 GstPad *sinkpad = NULL;
1970 wfd_sink_debug_fenter();
1972 wfd_sink_return_val_if_fail(wfd_sink &&
1973 wfd_sink->pipeline &&
1974 wfd_sink->pipeline->mainbin &&
1975 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst,
1976 MM_ERROR_WFD_NOT_INITIALIZED);
1978 if (wfd_sink->stream_info.audio_stream_info.codec == MM_WFD_SINK_AUDIO_CODEC_NONE) {
1979 wfd_sink_debug("Skip prepare audio pipeline for none audio codec");
1980 wfd_sink_debug_fleave();
1981 return MM_ERROR_NONE;
1984 /* check audio decodebin is linked */
1985 if (!wfd_sink->audio_decodebin_is_linked) {
1986 /* check audio decodebin is created */
1987 if (wfd_sink->pipeline->a_decodebin == NULL) {
1988 if (MM_ERROR_NONE != __mm_wfd_sink_create_audio_decodebin(wfd_sink)) {
1989 wfd_sink_error("failed to create audio decodebin....");
1994 if (MM_ERROR_NONE != __mm_wfd_sink_link_audio_decodebin(wfd_sink)) {
1995 wfd_sink_error("failed to link audio decodebin.....");
2000 /* check audio sinkbin is created */
2001 if (wfd_sink->pipeline->a_sinkbin == NULL) {
2002 if (MM_ERROR_NONE != __mm_wfd_sink_create_audio_sinkbin(wfd_sink)) {
2003 wfd_sink_error("failed to create audio sinkbin....");
2008 /* add audio decodebin to pipeline */
2009 if (wfd_sink->pipeline->a_decodebin && wfd_sink->pipeline->a_decodebin[WFD_SINK_A_D_BIN].gst) {
2010 a_decodebin = wfd_sink->pipeline->a_decodebin[WFD_SINK_A_D_BIN].gst;
2012 pipeline = GST_ELEMENT_CAST(gst_element_get_parent(a_decodebin));
2014 pipeline = wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst;
2016 if (GST_STATE(a_decodebin) <= GST_STATE_NULL) {
2017 wfd_sink_debug("need to prepare %s", GST_ELEMENT_NAME(a_decodebin));
2018 if (GST_STATE_CHANGE_FAILURE == gst_element_set_state(a_decodebin, GST_STATE_READY)) {
2019 wfd_sink_error("failed to set state(READY) to audio decodebin");
2024 wfd_sink_debug("try to add %s to %s",
2025 GST_ELEMENT_NAME(a_decodebin), GST_ELEMENT_NAME(pipeline));
2026 if (!gst_bin_add(GST_BIN(pipeline), GST_ELEMENT(a_decodebin))) {
2027 wfd_sink_error("failed to add %s to %s",
2028 GST_ELEMENT_NAME(a_decodebin), GST_ELEMENT_NAME(pipeline));
2032 wfd_sink_debug("need to sync state %s with its parent", GST_ELEMENT_NAME(a_decodebin));
2033 if (!gst_element_sync_state_with_parent(GST_ELEMENT(a_decodebin))) {
2034 wfd_sink_error("failed to sync %s state with parent", GST_ELEMENT_NAME(a_decodebin));
2038 wfd_sink_debug("%s is already added to %s",
2039 GST_ELEMENT_NAME(a_decodebin), GST_ELEMENT_NAME(pipeline));
2040 gst_object_unref(pipeline);
2044 wfd_sink_warning("going on without audio decodebin....");
2047 /* add audio sinkbin to pipeline */
2048 if (wfd_sink->pipeline->a_sinkbin && wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_BIN].gst) {
2049 a_sinkbin = wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_BIN].gst;
2051 pipeline = GST_ELEMENT_CAST(gst_element_get_parent(a_sinkbin));
2053 pipeline = wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst;
2055 /* prepare audio sinkbin before adding */
2056 if (GST_STATE(a_sinkbin) <= GST_STATE_NULL) {
2057 wfd_sink_debug("need to prepare %s", GST_ELEMENT_NAME(a_sinkbin));
2058 if (GST_STATE_CHANGE_FAILURE == gst_element_set_state(a_sinkbin, GST_STATE_READY)) {
2059 wfd_sink_error("failed to set state(READY) to audio sinkbin");
2064 /* add audio sinkbin to pipeline */
2065 wfd_sink_debug("try to add %s to %s",
2066 GST_ELEMENT_NAME(a_sinkbin), GST_ELEMENT_NAME(pipeline));
2067 if (!gst_bin_add(GST_BIN(pipeline), GST_ELEMENT(a_sinkbin))) {
2068 wfd_sink_error("failed to add %s to %s",
2069 GST_ELEMENT_NAME(a_sinkbin), GST_ELEMENT_NAME(pipeline));
2073 /* sync state with parent */
2074 wfd_sink_debug("need to sync state %s with its parent", GST_ELEMENT_NAME(a_sinkbin));
2075 if (!gst_element_sync_state_with_parent(GST_ELEMENT(a_sinkbin))) {
2076 wfd_sink_error("failed to sync %s state with parent", GST_ELEMENT_NAME(a_sinkbin));
2080 wfd_sink_debug("%s is already added to %s",
2081 GST_ELEMENT_NAME(a_sinkbin), GST_ELEMENT_NAME(pipeline));
2082 gst_object_unref(pipeline);
2086 wfd_sink_warning("going on without audio sinkbin....");
2090 /* link audio decodebin and sinkbin */
2091 if (wfd_sink->pipeline->a_decodebin && wfd_sink->pipeline->a_decodebin[WFD_SINK_A_D_BIN].gst) {
2092 a_decodebin = wfd_sink->pipeline->a_decodebin[WFD_SINK_A_D_BIN].gst;
2095 *pad = gst_element_get_static_pad(a_decodebin, "sink");
2097 if (wfd_sink->pipeline->a_sinkbin && wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_BIN].gst) {
2099 a_sinkbin = wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_BIN].gst;
2101 srcpad = gst_element_get_static_pad(a_decodebin, "src");
2103 wfd_sink_error("faied to get srcpad from %s", GST_ELEMENT_NAME(a_decodebin));
2107 if (!gst_pad_is_linked(srcpad)) {
2108 sinkpad = gst_element_get_static_pad(a_sinkbin, "sink");
2110 wfd_sink_error("faied to get sinkpad from %s", GST_ELEMENT_NAME(a_sinkbin));
2114 wfd_sink_debug("try to link %s:%s and %s:%s",
2115 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
2116 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
2117 if (GST_PAD_LINK_OK != gst_pad_link_full(srcpad, sinkpad, GST_PAD_LINK_CHECK_NOTHING)) {
2118 wfd_sink_error("failed to link %s:%s and %s:%s",
2119 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
2120 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
2124 gst_object_unref(sinkpad);
2127 gst_object_unref(srcpad);
2130 } else if (wfd_sink->pipeline->a_sinkbin && wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_BIN].gst) {
2131 a_sinkbin = wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_BIN].gst;
2133 *pad = gst_element_get_static_pad(a_sinkbin, "sink");
2136 wfd_sink_debug_fleave();
2138 return MM_ERROR_NONE;
2143 gst_object_unref(pipeline);
2148 gst_object_unref(sinkpad);
2153 gst_object_unref(srcpad);
2157 /* need to notify to app */
2158 MMWFDSINK_POST_MESSAGE(wfd_sink,
2159 MM_ERROR_WFD_INTERNAL,
2160 MMWFDSINK_CURRENT_STATE(wfd_sink));
2162 return MM_ERROR_WFD_INTERNAL;
2165 #define COMPENSATION_CRETERIA_VALUE 1000000 /* 1 msec */
2166 #define COMPENSATION_CHECK_PERIOD (30*GST_SECOND) /* 30 sec */
2168 static GstPadProbeReturn
2169 _mm_wfd_sink_check_running_time(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
2171 mm_wfd_sink_t *wfd_sink = (mm_wfd_sink_t *)u_data;
2172 GstClockTime current_time = GST_CLOCK_TIME_NONE;
2173 GstClockTime start_time = GST_CLOCK_TIME_NONE;
2174 GstClockTime running_time = GST_CLOCK_TIME_NONE;
2175 GstClockTime base_time = GST_CLOCK_TIME_NONE;
2176 GstClockTime render_time = GST_CLOCK_TIME_NONE;
2177 GstClockTimeDiff diff = GST_CLOCK_TIME_NONE;
2178 GstBuffer *buffer = NULL;
2179 gint64 ts_offset = 0LL;
2181 wfd_sink_return_val_if_fail(info, FALSE);
2182 wfd_sink_return_val_if_fail(wfd_sink &&
2183 wfd_sink->pipeline &&
2184 wfd_sink->pipeline->mainbin &&
2185 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst,
2186 GST_PAD_PROBE_DROP);
2188 if (!wfd_sink->clock) {
2189 wfd_sink_warning("pipeline did not select clock, yet");
2190 return GST_PAD_PROBE_OK;
2193 if (wfd_sink->need_to_reset_basetime)
2194 _mm_wfd_sink_reset_basetime(wfd_sink);
2196 /* calculate current runninig time */
2197 current_time = gst_clock_get_time(wfd_sink->clock);
2198 if (g_strrstr(GST_OBJECT_NAME(pad), "video"))
2199 base_time = gst_element_get_base_time(wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_BIN].gst);
2200 else if (g_strrstr(GST_OBJECT_NAME(pad), "audio"))
2201 base_time = gst_element_get_base_time(wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_BIN].gst);
2202 start_time = gst_element_get_start_time(wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst);
2203 if (GST_CLOCK_TIME_IS_VALID(current_time) &&
2204 GST_CLOCK_TIME_IS_VALID(start_time) &&
2205 GST_CLOCK_TIME_IS_VALID(base_time)) {
2206 running_time = current_time - (start_time + base_time);
2208 wfd_sink_debug("current time %"GST_TIME_FORMAT", start time %"GST_TIME_FORMAT
2209 " base time %"GST_TIME_FORMAT"", GST_TIME_ARGS(current_time),
2210 GST_TIME_ARGS(start_time), GST_TIME_ARGS(base_time));
2211 return GST_PAD_PROBE_OK;
2214 /* calculate this buffer rendering time */
2215 buffer = gst_pad_probe_info_get_buffer(info);
2216 if (!GST_BUFFER_TIMESTAMP_IS_VALID(buffer)) {
2217 wfd_sink_warning("buffer timestamp is invalid.");
2218 return GST_PAD_PROBE_OK;
2221 if (g_strrstr(GST_OBJECT_NAME(pad), "audio")) {
2222 if (wfd_sink->pipeline && wfd_sink->pipeline->a_sinkbin && wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_SINK].gst)
2223 g_object_get(G_OBJECT(wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_SINK].gst), "ts-offset", &ts_offset, NULL);
2224 } else if (g_strrstr(GST_OBJECT_NAME(pad), "video")) {
2225 if (wfd_sink->pipeline && wfd_sink->pipeline->v_sinkbin && wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_SINK].gst)
2226 g_object_get(G_OBJECT(wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_SINK].gst), "ts-offset", &ts_offset, NULL);
2229 render_time = GST_BUFFER_TIMESTAMP(buffer);
2230 render_time += ts_offset;
2232 /* chekc this buffer could be rendered or not */
2233 if (GST_CLOCK_TIME_IS_VALID(running_time) && GST_CLOCK_TIME_IS_VALID(render_time)) {
2234 diff = GST_CLOCK_DIFF(running_time, render_time);
2236 /* this buffer could be NOT rendered */
2237 wfd_sink_debug("%s : diff time : -%" GST_TIME_FORMAT "",
2238 GST_STR_NULL((GST_OBJECT_NAME(pad))),
2239 GST_TIME_ARGS(GST_CLOCK_DIFF(render_time, running_time)));
2241 /* this buffer could be rendered */
2242 /*wfd_sink_debug ("%s :diff time : %" GST_TIME_FORMAT "", */
2243 /* GST_STR_NULL((GST_OBJECT_NAME(pad))), */
2244 /* GST_TIME_ARGS(diff)); */
2248 /* update buffer count and gap */
2249 if (g_strrstr(GST_OBJECT_NAME(pad), "video")) {
2250 wfd_sink->video_buffer_count++;
2251 wfd_sink->video_accumulated_gap += diff;
2252 } else if (g_strrstr(GST_OBJECT_NAME(pad), "audio")) {
2253 wfd_sink->audio_buffer_count++;
2254 wfd_sink->audio_accumulated_gap += diff;
2256 wfd_sink_warning("invalid buffer type.. ");
2257 return GST_PAD_PROBE_DROP;
2260 if (GST_CLOCK_TIME_IS_VALID(wfd_sink->last_buffer_timestamp)) {
2261 /* fisrt 60sec, just calculate the gap between source device and sink device */
2262 if (GST_BUFFER_TIMESTAMP(buffer) < 60 * GST_SECOND)
2263 return GST_PAD_PROBE_OK;
2265 /* every 10sec, calculate the gap between source device and sink device */
2266 if (GST_CLOCK_DIFF(wfd_sink->last_buffer_timestamp, GST_BUFFER_TIMESTAMP(buffer))
2267 > COMPENSATION_CHECK_PERIOD) {
2268 gint64 audio_avgrage_gap = 0LL;
2269 gint64 video_avgrage_gap = 0LL;
2270 gint64 audio_avgrage_gap_diff = 0LL;
2271 gint64 video_avgrage_gap_diff = 0LL;
2272 gboolean video_minus_compensation = FALSE;
2273 gboolean audio_minus_compensation = FALSE;
2274 gint64 avgrage_gap_diff = 0LL;
2275 gboolean minus_compensation = FALSE;
2278 if (wfd_sink->video_buffer_count > 0) {
2279 video_avgrage_gap = wfd_sink->video_accumulated_gap / wfd_sink->video_buffer_count;
2281 if (wfd_sink->video_average_gap != 0) {
2282 if (video_avgrage_gap > wfd_sink->video_average_gap) {
2283 video_avgrage_gap_diff = video_avgrage_gap - wfd_sink->video_average_gap;
2284 video_minus_compensation = TRUE;
2286 video_avgrage_gap_diff = wfd_sink->video_average_gap - video_avgrage_gap;
2287 video_minus_compensation = FALSE;
2290 wfd_sink_debug("first update video average gap(%lld) ", video_avgrage_gap);
2291 wfd_sink->video_average_gap = video_avgrage_gap;
2294 wfd_sink_debug("there is no video buffer flow during %"GST_TIME_FORMAT
2295 " ~ %" GST_TIME_FORMAT"",
2296 GST_TIME_ARGS(wfd_sink->last_buffer_timestamp),
2297 GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(buffer)));
2301 if (wfd_sink->audio_buffer_count > 0) {
2302 audio_avgrage_gap = wfd_sink->audio_accumulated_gap / wfd_sink->audio_buffer_count;
2304 if (wfd_sink->audio_average_gap != 0) {
2305 if (audio_avgrage_gap > wfd_sink->audio_average_gap) {
2306 audio_avgrage_gap_diff = audio_avgrage_gap - wfd_sink->audio_average_gap;
2307 audio_minus_compensation = TRUE;
2309 audio_avgrage_gap_diff = wfd_sink->audio_average_gap - audio_avgrage_gap;
2310 audio_minus_compensation = FALSE;
2313 wfd_sink_debug("first update audio average gap(%lld) ", audio_avgrage_gap);
2314 wfd_sink->audio_average_gap = audio_avgrage_gap;
2317 wfd_sink_debug("there is no audio buffer flow during %"GST_TIME_FORMAT
2318 " ~ %" GST_TIME_FORMAT"",
2319 GST_TIME_ARGS(wfd_sink->last_buffer_timestamp),
2320 GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(buffer)));
2323 /* selecet average_gap_diff between video and audio */
2324 /* which makes no buffer drop in the sink elements */
2325 if (video_avgrage_gap_diff && audio_avgrage_gap_diff) {
2326 if (!video_minus_compensation && !audio_minus_compensation) {
2327 minus_compensation = FALSE;
2328 if (video_avgrage_gap_diff > audio_avgrage_gap_diff)
2329 avgrage_gap_diff = video_avgrage_gap_diff;
2331 avgrage_gap_diff = audio_avgrage_gap_diff;
2332 } else if (video_minus_compensation && audio_minus_compensation) {
2333 minus_compensation = TRUE;
2334 if (video_avgrage_gap_diff > audio_avgrage_gap_diff)
2335 avgrage_gap_diff = audio_avgrage_gap_diff;
2337 avgrage_gap_diff = video_avgrage_gap_diff;
2339 minus_compensation = FALSE;
2340 if (!video_minus_compensation)
2341 avgrage_gap_diff = video_avgrage_gap_diff;
2343 avgrage_gap_diff = audio_avgrage_gap_diff;
2345 } else if (video_avgrage_gap_diff) {
2346 minus_compensation = video_minus_compensation;
2347 avgrage_gap_diff = video_avgrage_gap_diff;
2348 } else if (audio_avgrage_gap_diff) {
2349 minus_compensation = audio_minus_compensation;
2350 avgrage_gap_diff = audio_avgrage_gap_diff;
2353 wfd_sink_debug("average diff gap difference beween audio:%s%lld and video:%s%lld ",
2354 audio_minus_compensation ? "-" : "", audio_avgrage_gap_diff,
2355 video_minus_compensation ? "-" : "", video_avgrage_gap_diff);
2358 /* if calculated gap diff is larger than 1ms. need to compensate buffer timestamp */
2359 if (avgrage_gap_diff >= COMPENSATION_CRETERIA_VALUE) {
2360 if (minus_compensation)
2361 ts_offset -= avgrage_gap_diff;
2363 ts_offset += avgrage_gap_diff;
2365 wfd_sink_debug("do timestamp compensation : %s%lld (ts-offset : %"
2366 GST_TIME_FORMAT") at(%" GST_TIME_FORMAT")",
2367 minus_compensation ? "-" : "", avgrage_gap_diff,
2368 GST_TIME_ARGS(ts_offset), GST_TIME_ARGS(running_time));
2370 if (wfd_sink->pipeline && wfd_sink->pipeline->a_sinkbin && wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_SINK].gst)
2371 g_object_set(G_OBJECT(wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_SINK].gst), "ts-offset", (gint64)ts_offset, NULL);
2372 if (wfd_sink->pipeline && wfd_sink->pipeline->v_sinkbin && wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_SINK].gst)
2373 g_object_set(G_OBJECT(wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_SINK].gst), "ts-offset", (gint64)ts_offset, NULL);
2375 wfd_sink_debug("don't need to do timestamp compensation : %s%lld (ts-offset : %"GST_TIME_FORMAT ")",
2376 minus_compensation ? "-" : "", avgrage_gap_diff, GST_TIME_ARGS(ts_offset));
2380 wfd_sink->video_buffer_count = 0;
2381 wfd_sink->video_accumulated_gap = 0LL;
2382 wfd_sink->audio_buffer_count = 0;
2383 wfd_sink->audio_accumulated_gap = 0LL;
2384 wfd_sink->last_buffer_timestamp = GST_BUFFER_TIMESTAMP(buffer);
2387 wfd_sink_debug("first update last buffer timestamp :%" GST_TIME_FORMAT,
2388 GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(buffer)));
2389 wfd_sink->last_buffer_timestamp = GST_BUFFER_TIMESTAMP(buffer);
2392 return GST_PAD_PROBE_OK;
2397 __mm_wfd_sink_demux_pad_added(GstElement *demux, GstPad *pad, gpointer data)
2399 mm_wfd_sink_t *wfd_sink = (mm_wfd_sink_t *)data;
2401 GstElement *pipeline = NULL;
2402 GstElement *valve = NULL;
2403 GstPad *sinkpad = NULL;
2404 GstPad *srcpad = NULL;
2406 wfd_sink_debug_fenter();
2408 wfd_sink_return_if_fail(wfd_sink &&
2409 wfd_sink->pipeline &&
2410 wfd_sink->pipeline->mainbin &&
2411 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst);
2413 name = gst_pad_get_name(pad);
2415 wfd_sink_error("fail to get pad");
2419 wfd_sink_debug("Mux pad added, session mode = %d, name[0] = %c", wfd_sink->ini.sink_session_mode, name[0]);
2421 //In case of 'audio-only-mirroring', we don't add video pad
2422 if ((wfd_sink->ini.sink_session_mode & WFD_SESSION_MODE_AUDIO_ONLY) && name[0] == 'v') {
2423 wfd_sink_error("Skip video pad add in audio only mode");
2428 //In case of none audio codec, we don't add audio pad
2429 if (wfd_sink->stream_info.audio_stream_info.codec == MM_WFD_SINK_AUDIO_CODEC_NONE && name[0] == 'a') {
2430 wfd_sink_error("Skip audio pad add for none audio codec");
2435 pipeline = wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst;
2437 /* take srcpad from demuxer added pad */
2438 srcpad = gst_object_ref(pad);
2440 if (name[0] == 'v') {
2441 if (wfd_sink->pipeline->mainbin[WFD_SINK_M_V_VALVE].gst)
2442 valve = wfd_sink->pipeline->mainbin[WFD_SINK_M_V_VALVE].gst;
2443 } else if (name[0] == 'a') {
2444 if (wfd_sink->pipeline->mainbin[WFD_SINK_M_A_VALVE].gst)
2445 valve = wfd_sink->pipeline->mainbin[WFD_SINK_M_A_VALVE].gst;
2448 /* add, link and run the valve */
2450 wfd_sink_debug("try to add %s to pipeline", GST_ELEMENT_NAME(valve));
2452 if (!gst_bin_add(GST_BIN(pipeline), valve)) {
2453 wfd_sink_error("failed to add %s to pipeline",
2454 GST_ELEMENT_NAME(valve));
2458 sinkpad = gst_element_get_static_pad(valve, "sink");
2460 wfd_sink_error("failed to get sink pad from %s",
2461 GST_ELEMENT_NAME(valve));
2465 wfd_sink_debug("try to link %s:%s and %s:%s",
2466 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
2467 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
2468 if (GST_PAD_LINK_OK != gst_pad_link_full(srcpad, sinkpad, GST_PAD_LINK_CHECK_NOTHING)) {
2469 wfd_sink_error("failed to link %s:%s and %s:%s",
2470 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
2471 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
2474 gst_object_unref(GST_OBJECT(srcpad));
2476 gst_object_unref(GST_OBJECT(sinkpad));
2479 wfd_sink_debug("try to sync %s's state with parent", GST_ELEMENT_NAME(valve));
2480 if (!gst_element_sync_state_with_parent(GST_ELEMENT_CAST(valve))) {
2481 wfd_sink_error("failed to sync %s state with parent",
2482 GST_PAD_NAME(valve));
2486 srcpad = gst_element_get_static_pad(valve, "src");
2488 wfd_sink_error("failed to get src pad from %s",
2489 GST_ELEMENT_NAME(valve));
2494 /* take decodebin/sinkbin */
2495 if (name[0] == 'v') {
2496 wfd_sink_debug("=========== >>>>>>>>>> Received VIDEO pad...");
2498 MMWFDSINK_PAD_PROBE(wfd_sink, pad, NULL, NULL);
2500 gst_pad_add_probe(pad,
2501 GST_PAD_PROBE_TYPE_BUFFER,
2502 _mm_wfd_sink_check_running_time,
2506 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_video_pipeline(wfd_sink, &sinkpad)) {
2507 wfd_sink_error("failed to prepare video pipeline....");
2510 } else if (name[0] == 'a') {
2511 wfd_sink_debug("=========== >>>>>>>>>> Received AUDIO pad...");
2513 MMWFDSINK_PAD_PROBE(wfd_sink, pad, NULL, NULL);
2515 gst_pad_add_probe(pad,
2516 GST_PAD_PROBE_TYPE_BUFFER,
2517 _mm_wfd_sink_check_running_time,
2521 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_audio_pipeline(wfd_sink, &sinkpad)) {
2522 wfd_sink_error("failed to prepare audio pipeline....");
2526 wfd_sink_error("unexceptable pad is added!!!");
2531 wfd_sink_debug("try to link %s:%s and %s:%s",
2532 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
2533 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
2534 if (GST_PAD_LINK_OK != gst_pad_link_full(srcpad, sinkpad, GST_PAD_LINK_CHECK_NOTHING)) {
2535 wfd_sink_error("failed to link %s:%s and %s:%s",
2536 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
2537 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
2541 if (name[0] == 'v') {
2542 MMWFDSINK_GENERATE_DOT_IF_ENABLED(wfd_sink, "video-pad-added-pipeline");
2543 } else if (name[0] == 'a') {
2544 MMWFDSINK_GENERATE_DOT_IF_ENABLED(wfd_sink, "audio-pad-added-pipeline");
2548 MMWFDSINK_FREEIF(name);
2551 gst_object_unref(GST_OBJECT(srcpad));
2555 gst_object_unref(GST_OBJECT(sinkpad));
2558 wfd_sink_debug_fleave();
2564 /* need to notify to app */
2565 MMWFDSINK_POST_MESSAGE(wfd_sink,
2566 MM_ERROR_WFD_INTERNAL,
2567 MMWFDSINK_CURRENT_STATE(wfd_sink));
2573 __mm_wfd_sink_change_av_format(GstElement *wfdsrc, gpointer *need_to_flush, gpointer data)
2575 mm_wfd_sink_t *wfd_sink = (mm_wfd_sink_t *)data;
2577 wfd_sink_debug_fenter();
2579 wfd_sink_return_if_fail(wfd_sink);
2580 wfd_sink_return_if_fail(need_to_flush);
2582 if (MMWFDSINK_CURRENT_STATE(wfd_sink) == MM_WFD_SINK_STATE_PLAYING) {
2583 wfd_sink_debug("need to flush pipeline");
2584 *need_to_flush = (gpointer) TRUE;
2586 wfd_sink_debug("don't need to flush pipeline");
2587 *need_to_flush = (gpointer) FALSE;
2591 wfd_sink_debug_fleave();
2596 __mm_wfd_sink_update_stream_info(GstElement *wfdsrc, GstStructure *str, gpointer data)
2598 mm_wfd_sink_t *wfd_sink = (mm_wfd_sink_t *)data;
2599 MMWFDSinkStreamInfo *stream_info = NULL;
2600 gint is_valid_audio_format = FALSE;
2601 gint is_valid_video_format = FALSE;
2602 gchar *audio_format;
2603 gchar *video_format;
2605 wfd_sink_debug_fenter();
2606 wfd_sink_return_if_fail(str && GST_IS_STRUCTURE(str));
2607 wfd_sink_return_if_fail(wfd_sink);
2609 stream_info = &wfd_sink->stream_info;
2611 if (gst_structure_has_field(str, "audio_format")) {
2612 is_valid_audio_format = TRUE;
2613 audio_format = g_strdup(gst_structure_get_string(str, "audio_format"));
2614 if (g_strrstr(audio_format, "AAC"))
2615 stream_info->audio_stream_info.codec = MM_WFD_SINK_AUDIO_CODEC_AAC;
2616 else if (g_strrstr(audio_format, "AC3"))
2617 stream_info->audio_stream_info.codec = MM_WFD_SINK_AUDIO_CODEC_AC3;
2618 else if (g_strrstr(audio_format, "LPCM"))
2619 stream_info->audio_stream_info.codec = MM_WFD_SINK_AUDIO_CODEC_LPCM;
2621 wfd_sink_error("invalid audio format(%s)...", audio_format);
2622 is_valid_audio_format = FALSE;
2624 if (is_valid_audio_format == TRUE) {
2625 if (gst_structure_has_field(str, "audio_rate"))
2626 gst_structure_get_int(str, "audio_rate", &stream_info->audio_stream_info.sample_rate);
2627 if (gst_structure_has_field(str, "audio_channels"))
2628 gst_structure_get_int(str, "audio_channels", &stream_info->audio_stream_info.channels);
2629 if (gst_structure_has_field(str, "audio_bitwidth"))
2630 gst_structure_get_int(str, "audio_bitwidth", &stream_info->audio_stream_info.bitwidth);
2632 WFD_SINK_MANAGER_APPEND_CMD(wfd_sink, WFD_SINK_MANAGER_CMD_PREPARE_A_PIPELINE);
2634 wfd_sink_debug("audio_format : %s \n \t rate : %d \n \t channels : %d \n \t bitwidth : %d \n \t \n",
2636 stream_info->audio_stream_info.sample_rate,
2637 stream_info->audio_stream_info.channels,
2638 stream_info->audio_stream_info.bitwidth);
2641 g_free (audio_format);
2643 if (gst_structure_has_field(str, "video_format")) {
2644 is_valid_video_format = TRUE;
2645 video_format = g_strdup(gst_structure_get_string(str, "video_format"));
2646 if (g_strrstr(video_format, "H264")) {
2647 stream_info->video_stream_info.codec = MM_WFD_SINK_VIDEO_CODEC_H264;
2648 } else if (g_strrstr(video_format, "H265")) {
2649 stream_info->video_stream_info.codec = MM_WFD_SINK_VIDEO_CODEC_H265;
2651 wfd_sink_error("invalid video format(%s)...", video_format);
2652 is_valid_video_format = FALSE;
2655 if (is_valid_video_format == TRUE) {
2656 if (gst_structure_has_field(str, "video_width"))
2657 gst_structure_get_int(str, "video_width", &stream_info->video_stream_info.width);
2658 if (gst_structure_has_field(str, "video_height"))
2659 gst_structure_get_int(str, "video_height", &stream_info->video_stream_info.height);
2660 if (gst_structure_has_field(str, "video_framerate"))
2661 gst_structure_get_int(str, "video_framerate", &stream_info->video_stream_info.frame_rate);
2663 WFD_SINK_MANAGER_APPEND_CMD(wfd_sink, WFD_SINK_MANAGER_CMD_PREPARE_V_PIPELINE);
2665 wfd_sink_debug("video_format : %s \n \t width : %d \n \t height : %d \n \t frame_rate : %d \n \t",
2667 stream_info->video_stream_info.width,
2668 stream_info->video_stream_info.height,
2669 stream_info->video_stream_info.frame_rate);
2672 g_free (video_format);
2675 WFD_SINK_MANAGER_SIGNAL_CMD(wfd_sink);
2677 wfd_sink_debug_fleave();
2680 static void __mm_wfd_sink_prepare_video_resolution(gint resolution, guint64 *CEA_resolution,
2681 guint64 *VESA_resolution, guint64 *HH_resolution)
2683 if (resolution == MM_WFD_SINK_RESOLUTION_UNKNOWN) return;
2685 *CEA_resolution = 0;
2686 *VESA_resolution = 0;
2689 if (resolution & MM_WFD_SINK_RESOLUTION_1920x1080_P30)
2690 *CEA_resolution |= WFD_CEA_1920x1080P30;
2692 if (resolution & MM_WFD_SINK_RESOLUTION_1280x720_P30)
2693 *CEA_resolution |= WFD_CEA_1280x720P30;
2695 if (resolution & MM_WFD_SINK_RESOLUTION_960x540_P30)
2696 *HH_resolution |= WFD_HH_960x540P30;
2698 if (resolution & MM_WFD_SINK_RESOLUTION_864x480_P30)
2699 *HH_resolution |= WFD_HH_864x480P30;
2701 if (resolution & MM_WFD_SINK_RESOLUTION_720x480_P60)
2702 *CEA_resolution |= WFD_CEA_720x480P60;
2704 if (resolution & MM_WFD_SINK_RESOLUTION_640x480_P60)
2705 *CEA_resolution |= WFD_CEA_640x480P60;
2707 if (resolution & MM_WFD_SINK_RESOLUTION_640x360_P30)
2708 *HH_resolution |= WFD_HH_640x360P30;
2711 static int __mm_wfd_sink_prepare_source(mm_wfd_sink_t *wfd_sink, GstElement *wfdsrc)
2713 GstStructure *wfd_audio_codecs = NULL;
2714 GstStructure *wfd_video_formats = NULL;
2715 GstStructure *wfd_content_protection = NULL;
2716 GstStructure *wfd2_video_formats = NULL;
2717 GstStructure *wfd2_audio_codecs = NULL;
2718 gint hdcp_version = 0;
2720 guint64 CEA_resolution = 0;
2721 guint64 VESA_resolution = 0;
2722 guint64 HH_resolution = 0;
2723 GObjectClass *klass;
2725 wfd_sink_debug_fenter();
2727 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
2728 wfd_sink_return_val_if_fail(wfd_sink->attrs, MM_ERROR_WFD_NOT_INITIALIZED);
2729 wfd_sink_return_val_if_fail(wfdsrc, MM_ERROR_WFD_NOT_INITIALIZED);
2731 klass = G_OBJECT_GET_CLASS(G_OBJECT(wfdsrc));
2733 if (g_object_class_find_property(klass, "enable-pad-probe")) /* for common wfdsrc */
2734 g_object_set(G_OBJECT(wfdsrc), "enable-pad-probe", wfd_sink->ini.trace_buffers_of_wfdsrc, NULL);
2735 g_object_set(G_OBJECT(wfdsrc), "udp-buffer-size", 2097152, NULL);
2736 g_object_set(G_OBJECT(wfdsrc), "latency", wfd_sink->ini.jitter_buffer_latency, NULL);
2737 g_object_set(G_OBJECT(wfdsrc), "user-agent", wfd_sink->ini.user_agent, NULL);
2738 g_object_set(G_OBJECT(wfdsrc), "dump-rtsp-message", wfd_sink->ini.dump_rtsp_message, NULL);
2739 if (g_object_class_find_property(klass, "dump-rtp-data"))
2740 g_object_set(G_OBJECT(wfdsrc), "dump-rtp-data", wfd_sink->ini.dump_rtp_data, NULL);
2741 if (g_object_class_find_property(klass, "trace-first-buffer"))
2742 g_object_set(G_OBJECT(wfdsrc), "trace-first-buffer", wfd_sink->ini.trace_first_buffer, NULL);
2743 if (g_object_class_find_property(klass, "trace-buffers"))
2744 g_object_set(G_OBJECT(wfdsrc), "trace-buffers", wfd_sink->ini.trace_buffers, NULL);
2745 if (g_object_class_find_property(klass, "do-request"))
2746 g_object_set(G_OBJECT(wfdsrc), "do-request", wfd_sink->ini.enable_retransmission, NULL);
2748 /* set audio parameter for Wi-Fi Display session negotiation */
2749 wfd_audio_codecs = gst_structure_new("wfd_audio_codecs",
2750 "audio_codec", G_TYPE_UINT, wfd_sink->ini.wfd_audio_codecs.audio_codec,
2751 "audio_latency", G_TYPE_UINT, wfd_sink->ini.wfd_audio_codecs.audio_latency,
2752 "audio_channels", G_TYPE_UINT, wfd_sink->ini.wfd_audio_codecs.audio_channel,
2753 "audio_sampling_frequency", G_TYPE_UINT, wfd_sink->ini.wfd_audio_codecs.audio_sampling_frequency,
2756 if (wfd_audio_codecs) {
2757 g_object_set(G_OBJECT(wfdsrc), "wfd-audio-codecs", wfd_audio_codecs, NULL);
2758 gst_structure_free(wfd_audio_codecs);
2759 wfd_audio_codecs = NULL;
2762 /* set video parameter for Wi-Fi Display session negotiation */
2763 CEA_resolution = wfd_sink->ini.wfd_video_formats.video_cea_support;
2764 VESA_resolution = wfd_sink->ini.wfd_video_formats.video_vesa_support;
2765 HH_resolution = wfd_sink->ini.wfd_video_formats.video_hh_support;
2767 __mm_wfd_sink_prepare_video_resolution(wfd_sink->supportive_resolution,
2768 &CEA_resolution, &VESA_resolution, &HH_resolution);
2769 wfd_video_formats = gst_structure_new("wfd_video_formats",
2770 "video_codec", G_TYPE_UINT, wfd_sink->ini.wfd_video_formats.video_codec,
2771 "video_native_resolution", G_TYPE_UINT, wfd_sink->ini.wfd_video_formats.video_native_resolution,
2772 "video_cea_support", G_TYPE_UINT64, CEA_resolution,
2773 "video_vesa_support", G_TYPE_UINT64, VESA_resolution,
2774 "video_hh_support", G_TYPE_UINT64, HH_resolution,
2775 "video_profile", G_TYPE_UINT, wfd_sink->ini.wfd_video_formats.video_profile,
2776 "video_level", G_TYPE_UINT, wfd_sink->ini.wfd_video_formats.video_level,
2777 "video_latency", G_TYPE_UINT, wfd_sink->ini.wfd_video_formats.video_latency,
2778 "video_vertical_resolution", G_TYPE_INT, wfd_sink->ini.wfd_video_formats.video_vertical_resolution,
2779 "video_horizontal_resolution", G_TYPE_INT, wfd_sink->ini.wfd_video_formats.video_horizontal_resolution,
2780 "video_minimum_slicing", G_TYPE_INT, wfd_sink->ini.wfd_video_formats.video_minimum_slicing,
2781 "video_slice_enc_param", G_TYPE_INT, wfd_sink->ini.wfd_video_formats.video_slice_enc_param,
2782 "video_framerate_control_support", G_TYPE_INT, wfd_sink->ini.wfd_video_formats.video_framerate_control_support,
2785 if (wfd_video_formats) {
2786 g_object_set(G_OBJECT(wfdsrc), "wfd-video-formats", wfd_video_formats, NULL);
2787 gst_structure_free(wfd_video_formats);
2788 wfd_video_formats = NULL;
2792 /* set hdcp parameter for Wi-Fi Display session negotiation */
2793 if (wfd_sink->ini.wfd_content_protection.enable_hdcp) {
2794 mm_attrs_get_int_by_name(wfd_sink->attrs, "hdcp_version", &hdcp_version);
2795 mm_attrs_get_int_by_name(wfd_sink->attrs, "hdcp_port", &hdcp_port);
2796 wfd_sink_debug("set hdcp version %d with %d port", hdcp_version, hdcp_port);
2798 wfd_content_protection = gst_structure_new("wfd_content_protection",
2799 "hdcp_version", G_TYPE_INT, hdcp_version,
2800 "hdcp_port_no", G_TYPE_INT, hdcp_port,
2803 if (wfd_content_protection) {
2804 g_object_set(G_OBJECT(wfdsrc), "wfd-content-protection", wfd_content_protection, NULL);
2805 gst_structure_free(wfd_content_protection);
2806 wfd_content_protection = NULL;
2810 if (g_object_class_find_property(klass, "wfd2-audio-codecs")) {
2811 /* set audio parameter for Wi-Fi Display R2 session negotiation */
2812 wfd2_audio_codecs = gst_structure_new("wfd2-audio-codecs",
2813 "audio_codec", G_TYPE_UINT, wfd_sink->ini.wfd2_audio_codecs.audio_codec,
2814 "audio_lpcm_mode", G_TYPE_UINT, wfd_sink->ini.wfd2_audio_codecs.audio_lpcm_mode,
2815 "audio_aac_mode", G_TYPE_UINT, wfd_sink->ini.wfd2_audio_codecs.audio_aac_mode,
2816 "audio_ac3_mode", G_TYPE_UINT, wfd_sink->ini.wfd2_audio_codecs.audio_ac3_mode,
2819 if (wfd2_audio_codecs) {
2820 g_object_set(G_OBJECT(wfdsrc), "wfd2-audio-codecs", wfd2_audio_codecs, NULL);
2821 gst_structure_free(wfd2_audio_codecs);
2822 wfd2_audio_codecs = NULL;
2826 if (g_object_class_find_property(klass, "wfd2-video-format-h264")) {
2827 /* set video parameter for Wi-Fi Display R2 session negotiation */
2828 CEA_resolution = wfd_sink->ini.wfd2_video_h264_info.video_cea_support;
2829 VESA_resolution = wfd_sink->ini.wfd2_video_h264_info.video_vesa_support;
2830 HH_resolution = wfd_sink->ini.wfd2_video_h264_info.video_hh_support;
2832 if (wfd_sink->ini.wfd2_video_formats.video_codec & WFD_VIDEO_H264) {
2833 wfd2_video_formats = gst_structure_new("wfd2-video-format-h264",
2834 "video_codec", G_TYPE_UINT, WFD_VIDEO_H264,
2835 "video_native_resolution", G_TYPE_UINT, wfd_sink->ini.wfd2_video_formats.video_native_resolution,
2836 "video_cea_support", G_TYPE_UINT64, CEA_resolution,
2837 "video_vesa_support", G_TYPE_UINT64, VESA_resolution,
2838 "video_hh_support", G_TYPE_UINT64, HH_resolution,
2839 "video_profile", G_TYPE_UINT, wfd_sink->ini.wfd2_video_h264_info.video_profile,
2840 "video_level", G_TYPE_UINT, wfd_sink->ini.wfd2_video_h264_info.video_level,
2841 "video_latency", G_TYPE_UINT, wfd_sink->ini.wfd2_video_h264_info.video_latency,
2842 "video_vertical_resolution", G_TYPE_INT, wfd_sink->ini.wfd2_video_h264_info.video_vertical_resolution,
2843 "video_horizontal_resolution", G_TYPE_INT, wfd_sink->ini.wfd2_video_h264_info.video_horizontal_resolution,
2844 "video_minimum_slicing", G_TYPE_INT, wfd_sink->ini.wfd2_video_h264_info.video_minimum_slicing,
2845 "video_slice_enc_param", G_TYPE_INT, wfd_sink->ini.wfd2_video_h264_info.video_slice_enc_param,
2846 "video_framerate_control_support", G_TYPE_INT, wfd_sink->ini.wfd2_video_h264_info.video_framerate_control_support,
2847 "video_non_transcoding_support", G_TYPE_INT, wfd_sink->ini.wfd2_video_formats.video_non_transcoding_support,
2850 if (wfd2_video_formats) {
2851 g_object_set(G_OBJECT(wfdsrc), "wfd2-video-format-h264", wfd2_video_formats, NULL);
2852 gst_structure_free(wfd2_video_formats);
2853 wfd2_video_formats = NULL;
2858 if (g_object_class_find_property(klass, "wfd2-video-format-h265")) {
2859 /* set video parameter for Wi-Fi Display R2 session negotiation */
2860 CEA_resolution = wfd_sink->ini.wfd2_video_h265_info.video_cea_support;
2861 VESA_resolution = wfd_sink->ini.wfd2_video_h265_info.video_vesa_support;
2862 HH_resolution = wfd_sink->ini.wfd2_video_h265_info.video_hh_support;
2864 if (wfd_sink->ini.wfd2_video_formats.video_codec & WFD_VIDEO_H265) {
2865 wfd2_video_formats = gst_structure_new("wfd2-video-format-h265",
2866 "video_codec", G_TYPE_UINT, WFD_VIDEO_H265,
2867 "video_native_resolution", G_TYPE_UINT, wfd_sink->ini.wfd2_video_formats.video_native_resolution,
2868 "video_cea_support", G_TYPE_UINT64, CEA_resolution,
2869 "video_vesa_support", G_TYPE_UINT64, VESA_resolution,
2870 "video_hh_support", G_TYPE_UINT64, HH_resolution,
2871 "video_profile", G_TYPE_UINT, wfd_sink->ini.wfd2_video_h265_info.video_profile,
2872 "video_level", G_TYPE_UINT, wfd_sink->ini.wfd2_video_h265_info.video_level,
2873 "video_latency", G_TYPE_UINT, wfd_sink->ini.wfd2_video_h265_info.video_latency,
2874 "video_vertical_resolution", G_TYPE_INT, wfd_sink->ini.wfd2_video_h265_info.video_vertical_resolution,
2875 "video_horizontal_resolution", G_TYPE_INT, wfd_sink->ini.wfd2_video_h265_info.video_horizontal_resolution,
2876 "video_minimum_slicing", G_TYPE_INT, wfd_sink->ini.wfd2_video_h265_info.video_minimum_slicing,
2877 "video_slice_enc_param", G_TYPE_INT, wfd_sink->ini.wfd2_video_h265_info.video_slice_enc_param,
2878 "video_framerate_control_support", G_TYPE_INT, wfd_sink->ini.wfd2_video_h265_info.video_framerate_control_support,
2879 "video_non_transcoding_support", G_TYPE_INT, wfd_sink->ini.wfd2_video_formats.video_non_transcoding_support,
2882 if (wfd2_video_formats) {
2883 g_object_set(G_OBJECT(wfdsrc), "wfd2-video-format-h265", wfd2_video_formats, NULL);
2884 gst_structure_free(wfd2_video_formats);
2885 wfd2_video_formats = NULL;
2890 wfd_sink->update_stream_info_sig_id = g_signal_connect(wfdsrc, "update-media-info",
2891 G_CALLBACK(__mm_wfd_sink_update_stream_info), wfd_sink);
2893 wfd_sink->change_av_format_sig_id = g_signal_connect(wfdsrc, "change-av-format",
2894 G_CALLBACK(__mm_wfd_sink_change_av_format), wfd_sink);
2896 wfd_sink_debug_fleave();
2898 return MM_ERROR_NONE;
2901 static int __mm_wfd_sink_prepare_demux(mm_wfd_sink_t *wfd_sink, GstElement *demux)
2903 wfd_sink_debug_fenter();
2905 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
2906 wfd_sink_return_val_if_fail(demux, MM_ERROR_WFD_NOT_INITIALIZED);
2908 g_signal_connect(demux, "pad-added",
2909 G_CALLBACK(__mm_wfd_sink_demux_pad_added), wfd_sink);
2911 wfd_sink_debug_fleave();
2913 return MM_ERROR_NONE;
2916 static void __mm_wfd_sink_queue_overrun(GstElement *queue, gpointer u_data)
2920 wfd_sink_debug_fenter();
2922 return_if_fail(queue);
2924 g_object_get(G_OBJECT(queue), "current-level-time", &time, NULL);
2926 wfd_sink_warning("%s is overrun(%" GST_TIME_FORMAT")",
2927 GST_ELEMENT_NAME(queue), GST_TIME_ARGS(time));
2929 wfd_sink_debug_fleave();
2934 static void __mm_wfd_sink_prepare_queue(mm_wfd_sink_t *wfd_sink, GstElement *queue)
2936 wfd_sink_debug_fenter();
2938 wfd_sink_return_if_fail(wfd_sink);
2939 wfd_sink_return_if_fail(queue);
2941 /* set maximum buffer size of queue as 3sec */
2942 g_object_set(G_OBJECT(queue), "max-size-bytes", 0, NULL);
2943 g_object_set(G_OBJECT(queue), "max-size-buffers", 0, NULL);
2944 g_object_set(G_OBJECT(queue), "max-size-time", (guint64)3000000000ULL, NULL);
2945 g_signal_connect(queue, "overrun",
2946 G_CALLBACK(__mm_wfd_sink_queue_overrun), wfd_sink);
2948 wfd_sink_debug_fleave();
2954 static int __mm_wfd_sink_create_pipeline(mm_wfd_sink_t *wfd_sink)
2956 MMWFDSinkGstElement *mainbin = NULL;
2957 GList *element_bucket = NULL;
2961 wfd_sink_debug_fenter();
2963 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
2964 wfd_sink_return_val_if_fail(wfd_sink->attrs, MM_ERROR_WFD_NOT_INITIALIZED);
2966 /* Create pipeline */
2967 wfd_sink->pipeline = (MMWFDSinkGstPipelineInfo *) g_malloc0(sizeof(MMWFDSinkGstPipelineInfo));
2968 if (wfd_sink->pipeline == NULL)
2971 memset(wfd_sink->pipeline, 0, sizeof(MMWFDSinkGstPipelineInfo));
2973 /* create mainbin */
2974 mainbin = (MMWFDSinkGstElement *) g_malloc0(sizeof(MMWFDSinkGstElement) * WFD_SINK_M_NUM);
2975 if (mainbin == NULL)
2978 memset(mainbin, 0, sizeof(MMWFDSinkGstElement) * WFD_SINK_M_NUM);
2980 /* create pipeline */
2981 mainbin[WFD_SINK_M_PIPE].id = WFD_SINK_M_PIPE;
2982 mainbin[WFD_SINK_M_PIPE].gst = gst_pipeline_new("wfdsink");
2983 if (!mainbin[WFD_SINK_M_PIPE].gst) {
2984 wfd_sink_error("failed to create pipeline");
2989 MMWFDSINK_CREATE_ELEMENT(mainbin, WFD_SINK_M_SRC, wfd_sink->ini.name_of_source, "wfdsink_source", TRUE);
2990 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_SRC].gst, "src");
2991 if (mainbin[WFD_SINK_M_SRC].gst) {
2992 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_source(wfd_sink, mainbin[WFD_SINK_M_SRC].gst)) {
2993 wfd_sink_error("failed to prepare wfdsrc...");
2998 /* create rtpmp2tdepay */
2999 MMWFDSINK_CREATE_ELEMENT(mainbin, WFD_SINK_M_DEPAY, "rtpmp2tdepay", "wfdsink_depay", TRUE);
3000 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_DEPAY].gst, "src");
3001 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_DEPAY].gst, "sink");
3003 MMWFDSINK_TS_DATA_DUMP(wfd_sink, mainbin[WFD_SINK_M_DEPAY].gst, "src");
3005 /* create queue for ts */
3006 MMWFDSINK_CREATE_ELEMENT(mainbin, WFD_SINK_M_QUEUE, "queue", "ts_queue", TRUE);
3007 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_QUEUE].gst, "src");
3008 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_QUEUE].gst, "sink");
3009 g_object_set(G_OBJECT(mainbin[WFD_SINK_M_QUEUE].gst), "max-size-buffers", 200000, NULL);
3011 /* create valve for demux */
3012 MMWFDSINK_CREATE_ELEMENT(mainbin, WFD_SINK_M_D_VALVE, "valve", "demux_valve", TRUE);
3013 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_D_VALVE].gst, "src");
3014 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_D_VALVE].gst, "sink");
3016 /* create tsdemuxer*/
3017 MMWFDSINK_CREATE_ELEMENT(mainbin, WFD_SINK_M_DEMUX, wfd_sink->ini.name_of_tsdemux, "wfdsink_demux", TRUE);
3018 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_DEMUX].gst, "sink");
3019 if (mainbin[WFD_SINK_M_DEMUX].gst) {
3020 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_demux(wfd_sink, mainbin[WFD_SINK_M_DEMUX].gst)) {
3021 wfd_sink_error("failed to prepare demux...");
3026 /* create valve for audio */
3027 MMWFDSINK_CREATE_ELEMENT(mainbin, WFD_SINK_M_A_VALVE, "valve", "audio_valve", FALSE);
3028 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_A_VALVE].gst, "src");
3029 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_A_VALVE].gst, "sink");
3031 if (wfd_sink->ini.sink_session_mode & (WFD_SESSION_MODE_MIXED | WFD_SESSION_MODE_VIDEO_ONLY)) {
3032 /* create valve for video */
3033 MMWFDSINK_CREATE_ELEMENT(mainbin, WFD_SINK_M_V_VALVE, "valve", "video_valve", FALSE);
3034 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_V_VALVE].gst, "src");
3035 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_V_VALVE].gst, "sink");
3038 /* adding created elements to pipeline */
3039 if (!__mm_wfd_sink_gst_element_add_bucket_to_bin(GST_BIN_CAST(mainbin[WFD_SINK_M_PIPE].gst), element_bucket, FALSE)) {
3040 wfd_sink_error("failed to add elements");
3044 /* linking elements in the bucket by added order. */
3045 if (__mm_wfd_sink_gst_element_link_bucket(element_bucket) == -1) {
3046 wfd_sink_error("failed to link elements");
3050 /* connect bus callback */
3051 bus = gst_pipeline_get_bus(GST_PIPELINE(mainbin[WFD_SINK_M_PIPE].gst));
3053 wfd_sink_error("cannot get bus from pipeline.");
3057 /* add bus message callback*/
3058 wfd_sink->msg_callback_id = gst_bus_add_watch(bus, (GstBusFunc)_mm_wfd_sink_msg_callback, wfd_sink);
3060 /* set sync handler to get tag synchronously */
3061 gst_bus_set_sync_handler(bus, _mm_wfd_bus_sync_callback, wfd_sink, NULL);
3063 g_list_free(element_bucket);
3064 element_bucket = NULL;
3065 gst_object_unref(GST_OBJECT(bus));
3068 /* now we have completed mainbin. take it */
3069 wfd_sink->pipeline->mainbin = mainbin;
3071 wfd_sink_debug_fleave();
3073 return MM_ERROR_NONE;
3077 wfd_sink_error("ERROR : releasing pipeline");
3079 if (element_bucket) {
3080 g_list_free(element_bucket);
3081 element_bucket = NULL;
3084 /* release element which are not added to bin */
3085 for (i = 1; i < WFD_SINK_M_NUM; i++) { /* NOTE : skip pipeline */
3086 if (mainbin != NULL && mainbin[i].gst) {
3087 GstObject *parent = NULL;
3088 parent = gst_element_get_parent(mainbin[i].gst);
3091 gst_object_unref(GST_OBJECT(mainbin[i].gst));
3092 mainbin[i].gst = NULL;
3094 gst_object_unref(GST_OBJECT(parent));
3100 /* release mainbin with it's childs */
3101 if (mainbin != NULL && mainbin[WFD_SINK_M_PIPE].gst) {
3102 gst_object_unref(GST_OBJECT(mainbin[WFD_SINK_M_PIPE].gst));
3103 mainbin[WFD_SINK_M_PIPE].gst = NULL;
3106 MMWFDSINK_FREEIF(mainbin);
3108 MMWFDSINK_FREEIF(wfd_sink->pipeline);
3110 return MM_ERROR_WFD_INTERNAL;
3113 int __mm_wfd_sink_link_audio_decodebin(mm_wfd_sink_t *wfd_sink)
3115 MMWFDSinkGstElement *a_decodebin = NULL;
3116 MMWFDSinkGstElement *first_element = NULL;
3117 MMWFDSinkGstElement *last_element = NULL;
3118 GList *element_bucket = NULL;
3119 GstPad *sinkpad = NULL;
3120 GstPad *srcpad = NULL;
3121 GstPad *ghostpad = NULL;
3122 GList *list_temp = NULL;
3124 wfd_sink_debug_fenter();
3126 wfd_sink_return_val_if_fail(wfd_sink &&
3127 wfd_sink->pipeline &&
3128 wfd_sink->pipeline->a_decodebin &&
3129 wfd_sink->pipeline->a_decodebin[WFD_SINK_A_D_BIN].gst,
3130 MM_ERROR_WFD_NOT_INITIALIZED);
3132 if (wfd_sink->audio_decodebin_is_linked) {
3133 wfd_sink_debug("audio decodebin is already linked... nothing to do");
3134 return MM_ERROR_NONE;
3137 if (wfd_sink->stream_info.audio_stream_info.codec == MM_WFD_SINK_AUDIO_CODEC_NONE) {
3138 wfd_sink_debug("Skip link audio decodebin for none audio codec.");
3139 wfd_sink_debug_fleave();
3140 return MM_ERROR_NONE;
3143 /* take audio decodebin */
3144 a_decodebin = wfd_sink->pipeline->a_decodebin;
3146 /* check audio queue */
3147 if (a_decodebin[WFD_SINK_A_D_QUEUE].gst)
3148 element_bucket = g_list_append(element_bucket, &a_decodebin[WFD_SINK_A_D_QUEUE]);
3150 /* check audio hdcp */
3151 if (a_decodebin[WFD_SINK_A_D_HDCP].gst)
3152 element_bucket = g_list_append(element_bucket, &a_decodebin[WFD_SINK_A_D_HDCP]);
3154 /* check audio codec */
3155 switch (wfd_sink->stream_info.audio_stream_info.codec) {
3156 case MM_WFD_SINK_AUDIO_CODEC_LPCM:
3157 if (a_decodebin[WFD_SINK_A_D_LPCM_CONVERTER].gst)
3158 element_bucket = g_list_append(element_bucket, &a_decodebin[WFD_SINK_A_D_LPCM_CONVERTER]);
3159 if (a_decodebin[WFD_SINK_A_D_LPCM_FILTER].gst) {
3160 GstCaps *caps = NULL;
3161 element_bucket = g_list_append(element_bucket, &a_decodebin[WFD_SINK_A_D_LPCM_FILTER]);
3162 caps = gst_caps_new_simple("audio/x-raw",
3163 "rate", G_TYPE_INT, wfd_sink->stream_info.audio_stream_info.sample_rate,
3164 "channels", G_TYPE_INT, wfd_sink->stream_info.audio_stream_info.channels,
3165 "format", G_TYPE_STRING, "S16BE", NULL);
3167 g_object_set(G_OBJECT(a_decodebin[WFD_SINK_A_D_LPCM_CONVERTER].gst), "caps", caps, NULL);
3168 gst_object_unref(GST_OBJECT(caps));
3173 case MM_WFD_SINK_AUDIO_CODEC_AAC:
3174 if (a_decodebin[WFD_SINK_A_D_AAC_PARSE].gst)
3175 element_bucket = g_list_append(element_bucket, &a_decodebin[WFD_SINK_A_D_AAC_PARSE]);
3176 if (a_decodebin[WFD_SINK_A_D_AAC_DEC].gst)
3177 element_bucket = g_list_append(element_bucket, &a_decodebin[WFD_SINK_A_D_AAC_DEC]);
3180 case MM_WFD_SINK_AUDIO_CODEC_AC3:
3181 if (a_decodebin[WFD_SINK_A_D_AC3_PARSE].gst)
3182 element_bucket = g_list_append(element_bucket, &a_decodebin[WFD_SINK_A_D_AC3_PARSE]);
3183 if (a_decodebin[WFD_SINK_A_D_AC3_DEC].gst)
3184 element_bucket = g_list_append(element_bucket, &a_decodebin[WFD_SINK_A_D_AC3_DEC]);
3188 wfd_sink_error("audio codec is not decied yet. cannot link audio decodebin...");
3189 return MM_ERROR_WFD_INTERNAL;
3193 if (element_bucket == NULL) {
3194 wfd_sink_error("there are no elements to be linked in the audio decodebin, destroy it");
3195 if (MM_ERROR_NONE != __mm_wfd_sink_destroy_audio_decodebin(wfd_sink)) {
3196 wfd_sink_error("failed to destroy audio decodebin");
3202 /* adding elements to audio decodebin */
3203 if (!__mm_wfd_sink_gst_element_add_bucket_to_bin(GST_BIN_CAST(a_decodebin[WFD_SINK_A_D_BIN].gst), element_bucket, FALSE)) {
3204 wfd_sink_error("failed to add elements to audio decodebin");
3208 /* linking elements in the bucket by added order. */
3209 if (__mm_wfd_sink_gst_element_link_bucket(element_bucket) == -1) {
3210 wfd_sink_error("failed to link elements of the audio decodebin");
3214 /* get first element's sinkpad for creating ghostpad */
3215 list_temp = g_list_first(element_bucket);
3216 if (list_temp == NULL) {
3217 wfd_sink_error("failed to get first list of the element_bucket");
3221 first_element = (MMWFDSinkGstElement *)list_temp->data;
3222 if (!first_element) {
3223 wfd_sink_error("failed to get first element of the audio decodebin");
3227 sinkpad = gst_element_get_static_pad(GST_ELEMENT(first_element->gst), "sink");
3229 wfd_sink_error("failed to get sink pad from element(%s)",
3230 GST_STR_NULL(GST_ELEMENT_NAME(first_element->gst)));
3234 ghostpad = gst_ghost_pad_new("sink", sinkpad);
3236 wfd_sink_error("failed to create ghostpad of audio decodebin");
3240 if (FALSE == gst_element_add_pad(a_decodebin[WFD_SINK_A_D_BIN].gst, ghostpad)) {
3241 wfd_sink_error("failed to add ghostpad to audio decodebin");
3244 gst_object_unref(GST_OBJECT(sinkpad));
3248 /* get last element's src for creating ghostpad */
3249 list_temp = g_list_last(element_bucket);
3250 if (list_temp == NULL) {
3251 wfd_sink_error("failed to get last list of the element_bucket");
3255 last_element = (MMWFDSinkGstElement *)list_temp->data;
3256 if (!last_element) {
3257 wfd_sink_error("failed to get last element of the audio decodebin");
3261 srcpad = gst_element_get_static_pad(GST_ELEMENT(last_element->gst), "src");
3263 wfd_sink_error("failed to get src pad from element(%s)",
3264 GST_STR_NULL(GST_ELEMENT_NAME(last_element->gst)));
3268 ghostpad = gst_ghost_pad_new("src", srcpad);
3270 wfd_sink_error("failed to create ghostpad of audio decodebin");
3274 if (FALSE == gst_element_add_pad(a_decodebin[WFD_SINK_A_D_BIN].gst, ghostpad)) {
3275 wfd_sink_error("failed to add ghostpad to audio decodebin");
3278 gst_object_unref(GST_OBJECT(srcpad));
3281 g_list_free(element_bucket);
3284 wfd_sink->audio_decodebin_is_linked = TRUE;
3286 wfd_sink_debug_fleave();
3288 return MM_ERROR_NONE;
3293 gst_object_unref(GST_OBJECT(srcpad));
3298 gst_object_unref(GST_OBJECT(sinkpad));
3302 g_list_free(element_bucket);
3304 return MM_ERROR_WFD_INTERNAL;
3307 static int __mm_wfd_sink_prepare_audiosink(mm_wfd_sink_t *wfd_sink, GstElement *audio_sink)
3309 wfd_sink_debug_fenter();
3311 /* check audiosink is created */
3312 wfd_sink_return_val_if_fail(audio_sink, MM_ERROR_WFD_INVALID_ARGUMENT);
3313 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
3315 g_object_set(G_OBJECT(audio_sink), "provide-clock", FALSE, NULL);
3316 g_object_set(G_OBJECT(audio_sink), "buffer-time", 100000LL, NULL);
3317 g_object_set(G_OBJECT(audio_sink), "slave-method", 2, NULL);
3318 g_object_set(G_OBJECT(audio_sink), "async", wfd_sink->ini.audio_sink_async, NULL);
3319 g_object_set(G_OBJECT(audio_sink), "ts-offset", (gint64)wfd_sink->ini.sink_ts_offset, NULL);
3321 wfd_sink_debug_fleave();
3323 return MM_ERROR_NONE;
3326 static int __mm_wfd_sink_destroy_audio_decodebin(mm_wfd_sink_t *wfd_sink)
3328 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
3329 MMWFDSinkGstElement *a_decodebin = NULL;
3330 GstObject *parent = NULL;
3333 wfd_sink_debug_fenter();
3335 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
3337 if (wfd_sink->pipeline &&
3338 wfd_sink->pipeline->a_decodebin &&
3339 wfd_sink->pipeline->a_decodebin[WFD_SINK_A_D_BIN].gst) {
3340 a_decodebin = wfd_sink->pipeline->a_decodebin;
3342 wfd_sink_debug("audio decodebin is not created, nothing to destroy");
3343 return MM_ERROR_NONE;
3346 parent = gst_element_get_parent(a_decodebin[WFD_SINK_A_D_BIN].gst);
3348 wfd_sink_debug("audio decodebin has no parent.. need to relase by itself");
3350 if (GST_STATE(a_decodebin[WFD_SINK_A_D_BIN].gst) >= GST_STATE_READY) {
3351 wfd_sink_debug("try to change state of audio decodebin to NULL");
3352 ret = gst_element_set_state(a_decodebin[WFD_SINK_A_D_BIN].gst, GST_STATE_NULL);
3353 if (ret != GST_STATE_CHANGE_SUCCESS) {
3354 wfd_sink_error("failed to change state of audio decodebin to NULL");
3355 return MM_ERROR_WFD_INTERNAL;
3359 /* release element which are not added to bin */
3360 for (i = 1; i < WFD_SINK_A_D_NUM; i++) { /* NOTE : skip bin */
3361 if (a_decodebin[i].gst) {
3362 parent = gst_element_get_parent(a_decodebin[i].gst);
3364 wfd_sink_debug("unref %s(current ref %d)",
3365 GST_STR_NULL(GST_ELEMENT_NAME(a_decodebin[i].gst)),
3366 ((GObject *) a_decodebin[i].gst)->ref_count);
3367 gst_object_unref(GST_OBJECT(a_decodebin[i].gst));
3368 a_decodebin[i].gst = NULL;
3370 wfd_sink_debug("%s has parent.(current ref %d)",
3371 GST_STR_NULL(GST_ELEMENT_NAME(a_decodebin[i].gst)),
3372 ((GObject *) a_decodebin[i].gst)->ref_count);
3373 gst_object_unref(GST_OBJECT(parent));
3379 /* release audio decodebin with it's childs */
3380 if (a_decodebin[WFD_SINK_A_D_BIN].gst) {
3381 gst_object_unref(GST_OBJECT(a_decodebin[WFD_SINK_A_D_BIN].gst));
3382 a_decodebin[WFD_SINK_A_D_BIN].gst = NULL;
3386 wfd_sink_debug("audio decodebin has parent(%s), unref it ",
3387 GST_STR_NULL(GST_OBJECT_NAME(GST_OBJECT(parent))));
3389 gst_object_unref(GST_OBJECT(parent));
3393 wfd_sink->audio_decodebin_is_linked = FALSE;
3395 MMWFDSINK_FREEIF(wfd_sink->pipeline->a_decodebin);
3397 wfd_sink_debug_fleave();
3399 return MM_ERROR_NONE;
3402 int __mm_wfd_sink_create_audio_decodebin(mm_wfd_sink_t *wfd_sink)
3404 MMWFDSinkGstElement *a_decodebin = NULL;
3405 gint audio_codec = WFD_AUDIO_UNKNOWN;
3406 GList *element_bucket = NULL;
3407 gboolean link = TRUE;
3410 wfd_sink_debug_fenter();
3412 wfd_sink_return_val_if_fail(wfd_sink &&
3414 MM_ERROR_WFD_NOT_INITIALIZED);
3416 if (wfd_sink->pipeline->a_decodebin != NULL) {
3417 wfd_sink_error("The audio decode bin is already created.");
3418 return MM_ERROR_NONE;
3421 if (wfd_sink->stream_info.audio_stream_info.codec == MM_WFD_SINK_AUDIO_CODEC_NONE) {
3422 wfd_sink_debug("Skip create audio decodebin for none audio codec.");
3423 wfd_sink_debug_fleave();
3424 return MM_ERROR_NONE;
3427 /* check audio decodebin could be linked now */
3428 switch (wfd_sink->stream_info.audio_stream_info.codec) {
3429 case MM_WFD_SINK_AUDIO_CODEC_AAC:
3430 audio_codec = WFD_AUDIO_AAC;
3433 case MM_WFD_SINK_AUDIO_CODEC_AC3:
3434 audio_codec = WFD_AUDIO_AC3;
3437 case MM_WFD_SINK_AUDIO_CODEC_LPCM:
3438 audio_codec = WFD_AUDIO_LPCM;
3441 case MM_WFD_SINK_AUDIO_CODEC_NONE:
3443 wfd_sink_debug("audio decodebin could NOT be linked now, just create");
3444 audio_codec = wfd_sink->ini.wfd_audio_codecs.audio_codec;
3450 a_decodebin = (MMWFDSinkGstElement *)g_malloc0(sizeof(MMWFDSinkGstElement) * WFD_SINK_A_D_NUM);
3452 wfd_sink_error("failed to allocate memory for audio decodebin");
3453 return MM_ERROR_WFD_NO_FREE_SPACE;
3456 memset(a_decodebin, 0, sizeof(MMWFDSinkGstElement) * WFD_SINK_A_D_NUM);
3458 /* create audio decodebin */
3459 a_decodebin[WFD_SINK_A_D_BIN].id = WFD_SINK_A_D_BIN;
3460 a_decodebin[WFD_SINK_A_D_BIN].gst = gst_bin_new("audio_deocebin");
3461 if (!a_decodebin[WFD_SINK_A_D_BIN].gst) {
3462 wfd_sink_error("failed to create audio decodebin");
3467 MMWFDSINK_CREATE_ELEMENT(a_decodebin, WFD_SINK_A_D_QUEUE, "queue", "audio_queue", FALSE);
3468 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_QUEUE].gst, "sink");
3469 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_QUEUE].gst, "src");
3470 if (a_decodebin[WFD_SINK_A_D_QUEUE].gst)
3471 __mm_wfd_sink_prepare_queue(wfd_sink, a_decodebin[WFD_SINK_A_D_QUEUE].gst);
3474 MMWFDSINK_CREATE_ELEMENT(a_decodebin, WFD_SINK_A_D_HDCP, wfd_sink->ini.name_of_audio_hdcp, "audio_hdcp", FALSE);
3475 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_HDCP].gst, "sink");
3476 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_HDCP].gst, "src");
3479 audio_codec = wfd_sink->ini.wfd_audio_codecs.audio_codec;
3480 if (audio_codec & WFD_AUDIO_LPCM) {
3481 /* create LPCM converter */
3482 MMWFDSINK_CREATE_ELEMENT(a_decodebin, WFD_SINK_A_D_LPCM_CONVERTER, wfd_sink->ini.name_of_lpcm_converter, "audio_lpcm_convert", FALSE);
3483 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_LPCM_CONVERTER].gst, "sink");
3484 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_LPCM_CONVERTER].gst, "src");
3486 /* create LPCM filter */
3487 MMWFDSINK_CREATE_ELEMENT(a_decodebin, WFD_SINK_A_D_LPCM_FILTER, wfd_sink->ini.name_of_lpcm_filter, "audio_lpcm_filter", FALSE);
3488 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_LPCM_FILTER].gst, "sink");
3489 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_LPCM_FILTER].gst, "src");
3492 if (audio_codec & WFD_AUDIO_AAC) {
3493 /* create AAC parse */
3494 MMWFDSINK_CREATE_ELEMENT(a_decodebin, WFD_SINK_A_D_AAC_PARSE, wfd_sink->ini.name_of_aac_parser, "audio_aac_parser", FALSE);
3495 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_AAC_PARSE].gst, "sink");
3496 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_AAC_PARSE].gst, "src");
3498 /* create AAC decoder */
3499 MMWFDSINK_CREATE_ELEMENT(a_decodebin, WFD_SINK_A_D_AAC_DEC, wfd_sink->ini.name_of_aac_decoder, "audio_aac_dec", FALSE);
3500 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_AAC_DEC].gst, "sink");
3501 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_AAC_DEC].gst, "src");
3504 if (audio_codec & WFD_AUDIO_AC3) {
3505 /* create AC3 parser */
3506 MMWFDSINK_CREATE_ELEMENT(a_decodebin, WFD_SINK_A_D_AC3_PARSE, wfd_sink->ini.name_of_ac3_parser, "audio_ac3_parser", FALSE);
3507 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_AC3_PARSE].gst, "sink");
3508 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_AC3_PARSE].gst, "src");
3510 /* create AC3 decoder */
3511 MMWFDSINK_CREATE_ELEMENT(a_decodebin, WFD_SINK_A_D_AC3_DEC, wfd_sink->ini.name_of_ac3_decoder, "audio_ac3_dec", FALSE);
3512 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_AC3_DEC].gst, "sink");
3513 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_AC3_DEC].gst, "src");
3516 g_list_free(element_bucket);
3519 wfd_sink->pipeline->a_decodebin = a_decodebin;
3521 /* link audio decodebin if audio codec is fixed */
3523 if (MM_ERROR_NONE != __mm_wfd_sink_link_audio_decodebin(wfd_sink)) {
3524 wfd_sink_error("failed to link audio decodebin, destroy audio decodebin");
3525 __mm_wfd_sink_destroy_audio_decodebin(wfd_sink);
3526 return MM_ERROR_WFD_INTERNAL;
3530 wfd_sink_debug_fleave();
3532 return MM_ERROR_NONE;
3535 wfd_sink_error("failed to create audio decodebin, release all");
3537 g_list_free(element_bucket);
3539 /* release element which are not added to bin */
3540 for (i = 1; i < WFD_SINK_A_D_NUM; i++) { /* NOTE : skip bin */
3541 if (a_decodebin != NULL && a_decodebin[i].gst) {
3542 GstObject *parent = NULL;
3543 parent = gst_element_get_parent(a_decodebin[i].gst);
3546 gst_object_unref(GST_OBJECT(a_decodebin[i].gst));
3547 a_decodebin[i].gst = NULL;
3549 gst_object_unref(GST_OBJECT(parent));
3555 /* release audio decodebin with it's childs */
3556 if (a_decodebin != NULL && a_decodebin[WFD_SINK_A_D_BIN].gst) {
3557 gst_object_unref(GST_OBJECT(a_decodebin[WFD_SINK_A_D_BIN].gst));
3558 a_decodebin[WFD_SINK_A_D_BIN].gst = NULL;
3561 MMWFDSINK_FREEIF(a_decodebin);
3563 return MM_ERROR_WFD_INTERNAL;
3566 static int __mm_wfd_sink_destroy_audio_sinkbin(mm_wfd_sink_t *wfd_sink)
3568 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
3569 MMWFDSinkGstElement *a_sinkbin = NULL;
3570 GstObject *parent = NULL;
3573 wfd_sink_debug_fenter();
3575 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
3577 if (wfd_sink->pipeline &&
3578 wfd_sink->pipeline->a_sinkbin &&
3579 wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_BIN].gst) {
3580 a_sinkbin = wfd_sink->pipeline->a_sinkbin;
3582 wfd_sink_debug("audio sinkbin is not created, nothing to destroy");
3583 return MM_ERROR_NONE;
3586 parent = gst_element_get_parent(a_sinkbin[WFD_SINK_A_S_BIN].gst);
3588 wfd_sink_debug("audio sinkbin has no parent.. need to relase by itself");
3590 if (GST_STATE(a_sinkbin[WFD_SINK_A_S_BIN].gst) >= GST_STATE_READY) {
3591 wfd_sink_debug("try to change state of audio sinkbin to NULL");
3592 ret = gst_element_set_state(a_sinkbin[WFD_SINK_A_S_BIN].gst, GST_STATE_NULL);
3593 if (ret != GST_STATE_CHANGE_SUCCESS) {
3594 wfd_sink_error("failed to change state of audio sinkbin to NULL");
3595 return MM_ERROR_WFD_INTERNAL;
3599 /* release element which are not added to bin */
3600 for (i = 1; i < WFD_SINK_A_S_NUM; i++) { /* NOTE : skip bin */
3601 if (a_sinkbin[i].gst) {
3602 parent = gst_element_get_parent(a_sinkbin[i].gst);
3604 wfd_sink_debug("unref %s(current ref %d)",
3605 GST_STR_NULL(GST_ELEMENT_NAME(a_sinkbin[i].gst)),
3606 ((GObject *) a_sinkbin[i].gst)->ref_count);
3607 gst_object_unref(GST_OBJECT(a_sinkbin[i].gst));
3608 a_sinkbin[i].gst = NULL;
3610 wfd_sink_debug("%s has parent.(current ref %d)",
3611 GST_STR_NULL(GST_ELEMENT_NAME(a_sinkbin[i].gst)),
3612 ((GObject *) a_sinkbin[i].gst)->ref_count);
3613 gst_object_unref(GST_OBJECT(parent));
3619 /* release audio sinkbin with it's childs */
3620 if (a_sinkbin[WFD_SINK_A_S_BIN].gst) {
3621 gst_object_unref(GST_OBJECT(a_sinkbin[WFD_SINK_A_S_BIN].gst));
3622 a_sinkbin[WFD_SINK_A_S_BIN].gst = NULL;
3625 wfd_sink_debug("audio sinkbin has parent(%s), unref it ",
3626 GST_STR_NULL(GST_OBJECT_NAME(GST_OBJECT(parent))));
3628 gst_object_unref(GST_OBJECT(parent));
3632 MMWFDSINK_FREEIF(wfd_sink->pipeline->a_sinkbin);
3634 wfd_sink_debug_fleave();
3636 return MM_ERROR_NONE;
3639 int __mm_wfd_sink_create_audio_sinkbin(mm_wfd_sink_t *wfd_sink)
3641 MMWFDSinkGstElement *a_sinkbin = NULL;
3642 MMWFDSinkGstElement *first_element = NULL;
3643 GList *element_bucket = NULL;
3644 GstPad *ghostpad = NULL;
3647 GList *list_temp = NULL;
3649 wfd_sink_debug_fenter();
3651 wfd_sink_return_val_if_fail(wfd_sink &&
3653 MM_ERROR_WFD_NOT_INITIALIZED);
3655 if (wfd_sink->pipeline->a_sinkbin != NULL) {
3656 wfd_sink_error("The audio sink bin is already created.");
3657 return MM_ERROR_NONE;
3660 if (wfd_sink->stream_info.audio_stream_info.codec == MM_WFD_SINK_AUDIO_CODEC_NONE) {
3661 wfd_sink_error("Skip create audio sink bin for non audio codec. %d", wfd_sink->stream_info.audio_stream_info.codec);
3662 wfd_sink_debug_fleave();
3663 return MM_ERROR_NONE;
3667 a_sinkbin = (MMWFDSinkGstElement *)g_malloc0(sizeof(MMWFDSinkGstElement) * WFD_SINK_A_S_NUM);
3669 wfd_sink_error("failed to allocate memory for audio sinkbin");
3670 return MM_ERROR_WFD_NO_FREE_SPACE;
3673 memset(a_sinkbin, 0, sizeof(MMWFDSinkGstElement) * WFD_SINK_A_S_NUM);
3675 /* create audio sinkbin */
3676 a_sinkbin[WFD_SINK_A_S_BIN].id = WFD_SINK_A_S_BIN;
3677 a_sinkbin[WFD_SINK_A_S_BIN].gst = gst_bin_new("audio_sinkbin");
3678 if (!a_sinkbin[WFD_SINK_A_S_BIN].gst) {
3679 wfd_sink_error("failed to create audio sinkbin");
3683 /* create resampler */
3684 MMWFDSINK_CREATE_ELEMENT(a_sinkbin, WFD_SINK_A_S_RESAMPLER,
3685 wfd_sink->ini.name_of_audio_resampler, "audio_resampler", TRUE);
3686 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_sinkbin[WFD_SINK_A_S_RESAMPLER].gst, "sink");
3687 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_sinkbin[WFD_SINK_A_S_RESAMPLER].gst, "src");
3690 MMWFDSINK_CREATE_ELEMENT(a_sinkbin, WFD_SINK_A_S_VOLUME,
3691 wfd_sink->ini.name_of_audio_volume, "audio_volume", TRUE);
3692 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_sinkbin[WFD_SINK_A_S_VOLUME].gst, "sink");
3693 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_sinkbin[WFD_SINK_A_S_VOLUME].gst, "src");
3695 MMWFDSINK_CREATE_ELEMENT(a_sinkbin, WFD_SINK_A_S_QUEUE,
3696 wfd_sink->ini.name_of_audio_sinkbin_queue, "audio_sinkbin_queue", TRUE);
3697 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_sinkbin[WFD_SINK_A_S_QUEUE].gst, "sink");
3698 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_sinkbin[WFD_SINK_A_S_QUEUE].gst, "src");
3701 MMWFDSINK_CREATE_ELEMENT(a_sinkbin, WFD_SINK_A_S_SINK,
3702 wfd_sink->ini.name_of_audio_sink, "audio_sink", TRUE);
3703 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_sinkbin[WFD_SINK_A_S_SINK].gst, "sink");
3704 if (a_sinkbin[WFD_SINK_A_S_SINK].gst) {
3705 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_audiosink(wfd_sink, a_sinkbin[WFD_SINK_A_S_SINK].gst)) {
3706 wfd_sink_error("failed to set audio sink property....");
3711 /* adding created elements to audio sinkbin */
3712 if (!__mm_wfd_sink_gst_element_add_bucket_to_bin(GST_BIN_CAST(a_sinkbin[WFD_SINK_A_S_BIN].gst), element_bucket, FALSE)) {
3713 wfd_sink_error("failed to add elements to audio sinkbin");
3717 /* linking elements in the bucket by added order. */
3718 if (__mm_wfd_sink_gst_element_link_bucket(element_bucket) == -1) {
3719 wfd_sink_error("failed to link elements fo the audio sinkbin");
3723 /* get first element's of the audio sinkbin */
3724 list_temp = g_list_first(element_bucket);
3725 if (list_temp == NULL) {
3726 wfd_sink_error("failed to get first list of the element_bucket");
3730 first_element = (MMWFDSinkGstElement *)list_temp->data;
3731 if (!first_element) {
3732 wfd_sink_error("failed to get first element of the audio sinkbin");
3736 /* get first element's sinkpad for creating ghostpad */
3737 pad = gst_element_get_static_pad(GST_ELEMENT(first_element->gst), "sink");
3739 wfd_sink_error("failed to get sink pad from element(%s)",
3740 GST_STR_NULL(GST_ELEMENT_NAME(first_element->gst)));
3744 ghostpad = gst_ghost_pad_new("sink", pad);
3746 wfd_sink_error("failed to create ghostpad of audio sinkbin");
3750 if (FALSE == gst_element_add_pad(a_sinkbin[WFD_SINK_A_S_BIN].gst, ghostpad)) {
3751 wfd_sink_error("failed to add ghostpad to audio sinkbin");
3754 gst_object_unref(GST_OBJECT(pad));
3757 g_list_free(element_bucket);
3758 element_bucket = NULL;
3761 wfd_sink->pipeline->a_sinkbin = a_sinkbin;
3763 wfd_sink_debug_fleave();
3765 return MM_ERROR_NONE;
3768 wfd_sink_error("failed to create audio sinkbin, releasing all");
3771 gst_object_unref(GST_OBJECT(pad));
3775 gst_object_unref(GST_OBJECT(ghostpad));
3778 if (element_bucket) {
3779 g_list_free(element_bucket);
3780 element_bucket = NULL;
3783 /* release element which are not added to bin */
3784 for (i = 1; i < WFD_SINK_A_S_NUM; i++) { /* NOTE : skip bin */
3785 if (a_sinkbin != NULL && a_sinkbin[i].gst) {
3786 GstObject *parent = NULL;
3787 parent = gst_element_get_parent(a_sinkbin[i].gst);
3790 gst_object_unref(GST_OBJECT(a_sinkbin[i].gst));
3791 a_sinkbin[i].gst = NULL;
3793 gst_object_unref(GST_OBJECT(parent));
3799 /* release audio sinkbin with it's childs */
3800 if (a_sinkbin != NULL && a_sinkbin[WFD_SINK_A_S_BIN].gst) {
3801 gst_object_unref(GST_OBJECT(a_sinkbin[WFD_SINK_A_S_BIN].gst));
3802 a_sinkbin[WFD_SINK_A_S_BIN].gst = NULL;
3804 MMWFDSINK_FREEIF(a_sinkbin);
3806 return MM_ERROR_WFD_INTERNAL;
3809 int __mm_wfd_sink_link_video_decodebin(mm_wfd_sink_t *wfd_sink)
3811 MMWFDSinkGstElement *v_decodebin = NULL;
3812 MMWFDSinkGstElement *first_element = NULL;
3813 MMWFDSinkGstElement *last_element = NULL;
3814 GList *element_bucket = NULL;
3815 GList *list_temp = NULL;
3816 GstPad *sinkpad = NULL;
3817 GstPad *srcpad = NULL;
3818 GstPad *ghostpad = NULL;
3820 wfd_sink_debug_fenter();
3822 wfd_sink_return_val_if_fail(wfd_sink &&
3823 wfd_sink->pipeline &&
3824 wfd_sink->pipeline->v_decodebin &&
3825 wfd_sink->pipeline->v_decodebin[WFD_SINK_V_D_BIN].gst,
3826 MM_ERROR_WFD_NOT_INITIALIZED);
3828 if (wfd_sink->video_decodebin_is_linked) {
3829 wfd_sink_debug("video decodebin is already linked... nothing to do");
3830 return MM_ERROR_NONE;
3833 /* take video decodebin */
3834 v_decodebin = wfd_sink->pipeline->v_decodebin;
3836 /* check video queue */
3837 if (v_decodebin[WFD_SINK_V_D_QUEUE].gst)
3838 element_bucket = g_list_append(element_bucket, &v_decodebin[WFD_SINK_V_D_QUEUE]);
3840 /* check video hdcp */
3841 if (v_decodebin[WFD_SINK_V_D_HDCP].gst)
3842 element_bucket = g_list_append(element_bucket, &v_decodebin[WFD_SINK_V_D_HDCP]);
3844 /* check video codec */
3845 switch (wfd_sink->stream_info.video_stream_info.codec) {
3846 case MM_WFD_SINK_VIDEO_CODEC_H264:
3847 if (v_decodebin[WFD_SINK_V_D_H264_PARSE].gst)
3848 element_bucket = g_list_append(element_bucket, &v_decodebin[WFD_SINK_V_D_H264_PARSE]);
3849 if (v_decodebin[WFD_SINK_V_D_H264_DEC].gst)
3850 element_bucket = g_list_append(element_bucket, &v_decodebin[WFD_SINK_V_D_H264_DEC]);
3853 case MM_WFD_SINK_VIDEO_CODEC_H265:
3854 if (v_decodebin[WFD_SINK_V_D_H265_PARSE].gst)
3855 element_bucket = g_list_append(element_bucket, &v_decodebin[WFD_SINK_V_D_H265_PARSE]);
3856 if (v_decodebin[WFD_SINK_V_D_H265_DEC].gst)
3857 element_bucket = g_list_append(element_bucket, &v_decodebin[WFD_SINK_V_D_H265_DEC]);
3861 wfd_sink_error("video codec is not decied yet. cannot link video decpdebin...");
3862 return MM_ERROR_WFD_INTERNAL;
3866 if (element_bucket == NULL) {
3867 wfd_sink_error("there are no elements to be linked in the video decodebin, destroy it");
3868 if (MM_ERROR_NONE != __mm_wfd_sink_destroy_video_decodebin(wfd_sink)) {
3869 wfd_sink_error("failed to destroy video decodebin");
3875 /* adding elements to video decodebin */
3876 if (!__mm_wfd_sink_gst_element_add_bucket_to_bin(GST_BIN_CAST(v_decodebin[WFD_SINK_V_D_BIN].gst), element_bucket, FALSE)) {
3877 wfd_sink_error("failed to add elements to video decodebin");
3881 /* linking elements in the bucket by added order. */
3882 if (__mm_wfd_sink_gst_element_link_bucket(element_bucket) == -1) {
3883 wfd_sink_error("failed to link elements of the video decodebin");
3887 /* get first element's sinkpad for creating ghostpad */
3888 list_temp = g_list_first(element_bucket);
3889 if (list_temp == NULL) {
3890 wfd_sink_error("failed to get first list of the element_bucket");
3894 first_element = (MMWFDSinkGstElement *)list_temp->data;
3895 if (!first_element) {
3896 wfd_sink_error("failed to get first element of the video decodebin");
3900 sinkpad = gst_element_get_static_pad(GST_ELEMENT(first_element->gst), "sink");
3902 wfd_sink_error("failed to get sink pad from element(%s)",
3903 GST_STR_NULL(GST_ELEMENT_NAME(first_element->gst)));
3907 ghostpad = gst_ghost_pad_new("sink", sinkpad);
3909 wfd_sink_error("failed to create ghostpad of video decodebin");
3913 if (FALSE == gst_element_add_pad(v_decodebin[WFD_SINK_V_D_BIN].gst, ghostpad)) {
3914 wfd_sink_error("failed to add ghostpad to video decodebin");
3917 gst_object_unref(GST_OBJECT(sinkpad));
3921 /* get last element's src for creating ghostpad */
3922 list_temp = g_list_last(element_bucket);
3923 if (list_temp == NULL) {
3924 wfd_sink_error("failed to get last list of the element_bucket");
3928 last_element = (MMWFDSinkGstElement *)list_temp->data;
3929 if (!last_element) {
3930 wfd_sink_error("failed to get last element of the video decodebin");
3934 srcpad = gst_element_get_static_pad(GST_ELEMENT(last_element->gst), "src");
3936 wfd_sink_error("failed to get src pad from element(%s)",
3937 GST_STR_NULL(GST_ELEMENT_NAME(last_element->gst)));
3941 ghostpad = gst_ghost_pad_new("src", srcpad);
3943 wfd_sink_error("failed to create ghostpad of video decodebin");
3947 if (FALSE == gst_element_add_pad(v_decodebin[WFD_SINK_V_D_BIN].gst, ghostpad)) {
3948 wfd_sink_error("failed to add ghostpad to video decodebin");
3951 gst_object_unref(GST_OBJECT(srcpad));
3954 g_list_free(element_bucket);
3957 wfd_sink->video_decodebin_is_linked = TRUE;
3959 wfd_sink_debug_fleave();
3961 return MM_ERROR_NONE;
3965 if (srcpad != NULL) {
3966 gst_object_unref(GST_OBJECT(srcpad));
3970 if (sinkpad != NULL) {
3971 gst_object_unref(GST_OBJECT(sinkpad));
3975 if (element_bucket != NULL) {
3976 g_list_free(element_bucket);
3977 element_bucket = NULL;
3979 return MM_ERROR_WFD_INTERNAL;
3982 static int __mm_wfd_sink_prepare_videodec(mm_wfd_sink_t *wfd_sink, GstElement *video_dec)
3984 wfd_sink_debug_fenter();
3986 /* check video decoder is created */
3987 wfd_sink_return_val_if_fail(video_dec, MM_ERROR_WFD_INVALID_ARGUMENT);
3988 wfd_sink_return_val_if_fail(wfd_sink && wfd_sink->attrs, MM_ERROR_WFD_NOT_INITIALIZED);
3990 wfd_sink_debug_fleave();
3992 return MM_ERROR_NONE;
3995 static int __mm_wfd_sink_prepare_videosink(mm_wfd_sink_t *wfd_sink, GstElement *video_sink)
3997 gboolean visible = TRUE;
3998 gint surface_type = MM_DISPLAY_SURFACE_OVERLAY;
4000 wfd_sink_debug_fenter();
4002 /* check videosink is created */
4003 wfd_sink_return_val_if_fail(video_sink, MM_ERROR_WFD_INVALID_ARGUMENT);
4004 wfd_sink_return_val_if_fail(wfd_sink && wfd_sink->attrs, MM_ERROR_WFD_NOT_INITIALIZED);
4006 /* update display surface */
4007 mm_attrs_get_int_by_name(wfd_sink->attrs, "display_surface_type", &surface_type);
4008 wfd_sink_info("check display surface type attribute: %d", surface_type);
4009 mm_attrs_get_int_by_name(wfd_sink->attrs, "display_visible", &visible);
4010 wfd_sink_info("check display visible attribute: %d", visible);
4012 if (FALSE == visible) {
4013 wfd_sink_info("skipped to prepare video sink. display_visible is FALSE.");
4014 g_object_set(G_OBJECT(video_sink), "visible", visible, NULL);
4015 return MM_ERROR_NONE;
4018 /* configuring display */
4019 switch (surface_type) {
4020 case MM_DISPLAY_SURFACE_EVAS: {
4021 void *object = NULL;
4024 /* common case if using evas surface */
4025 mm_attrs_get_data_by_name(wfd_sink->attrs, "display_overlay", &object);
4026 mm_attrs_get_int_by_name(wfd_sink->attrs, "display_evas_do_scaling", &scaling);
4028 wfd_sink_debug("set video param : evas-object %x", object);
4029 g_object_set(G_OBJECT(video_sink), "evas-object", object, NULL);
4031 wfd_sink_error("no evas object");
4032 return MM_ERROR_WFD_INTERNAL;
4037 case MM_DISPLAY_SURFACE_OVERLAY: {
4038 static unsigned int wl_surface_id = 0;
4039 static void *display_overlay = NULL;
4040 int wl_window_x = 0;
4041 int wl_window_y = 0;
4042 int wl_window_width = 0;
4043 int wl_window_height = 0;
4044 struct wl_surface *wl_surface = NULL;
4045 struct wl_display *wl_display = NULL;
4046 Ecore_Wl_Window *wl_window = NULL;
4047 wl_client *wlclient = NULL;
4048 Evas_Object *obj = NULL;
4049 void *object = NULL;
4050 const char *object_type = NULL;
4053 mm_attrs_get_data_by_name(wfd_sink->attrs, "display_overlay", &object);
4055 if (object != NULL) {
4056 obj = (Evas_Object *)object;
4057 object_type = evas_object_type_get(obj);
4058 wfd_sink_debug("window object type : %s", object_type);
4060 /* wayland overlay surface */
4061 LOGI("Wayland overlay surface type");
4062 evas_object_geometry_get(obj, &wl_window_x, &wl_window_y, &wl_window_width, &wl_window_height);
4064 wfd_sink_debug("x[%d] y[%d] width[%d] height[%d]", wl_window_x, wl_window_y,
4065 wl_window_width, wl_window_height);
4067 wl_window = elm_win_wl_window_get(obj);
4068 wl_surface = (struct wl_surface *) ecore_wl_window_surface_get(wl_window);
4070 /* get wl_display */
4071 wl_display = (struct wl_display *) ecore_wl_display_get();
4073 wfd_sink_debug("previous display object : %p current object : %p", display_overlay, object);
4074 if (wl_surface && wl_display && (wl_surface_id == 0 || display_overlay != object)) {
4075 wfd_sink_debug("surface = %p, wl_display = %p", wl_surface, wl_display);
4076 display_overlay = object;
4078 ret = mm_wfd_sink_wlclient_create(&wlclient);
4079 if (ret != MM_ERROR_NONE) {
4080 wfd_sink_error("Wayland client create failure");
4083 wfd_sink_debug("Try to get surface id");
4085 wl_surface_id = mm_wfd_sink_wlclient_get_wl_window_wl_surface_id(wlclient, wl_surface, wl_display);
4087 wfd_sink_debug("wl_surface_id = %d", wl_surface_id);
4094 wfd_sink_debug("set video param : surface_id %d", wl_surface_id);
4096 if (USE_EXTERNAL_WL_DISPLAY_HANDLE) {
4097 gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(video_sink),
4100 gst_video_overlay_set_wl_window_wl_surface_id(GST_VIDEO_OVERLAY(video_sink),
4104 /* After setting window handle, set render rectangle */
4105 gst_video_overlay_set_render_rectangle(GST_VIDEO_OVERLAY(video_sink),
4106 wl_window_x, wl_window_y, wl_window_width, wl_window_height);
4108 wfd_sink_debug("display object is NULL!");
4109 return MM_ERROR_WFD_INTERNAL;
4114 case MM_DISPLAY_SURFACE_NULL: {
4116 wfd_sink_error("Not Supported Surface.");
4117 return MM_ERROR_WFD_INTERNAL;
4121 wfd_sink_error("Not Supported Surface.(default case)");
4122 return MM_ERROR_WFD_INTERNAL;
4127 g_object_set(G_OBJECT(video_sink), "qos", FALSE, NULL);
4128 g_object_set(G_OBJECT(video_sink), "async", wfd_sink->ini.video_sink_async, NULL);
4129 g_object_set(G_OBJECT(video_sink), "max-lateness", (gint64)wfd_sink->ini.video_sink_max_lateness, NULL);
4130 g_object_set(G_OBJECT(video_sink), "visible", visible, NULL);
4131 g_object_set(G_OBJECT(video_sink), "ts-offset", (gint64)(wfd_sink->ini.sink_ts_offset), NULL);
4133 wfd_sink_debug_fleave();
4135 return MM_ERROR_NONE;
4138 static int __mm_wfd_sink_destroy_video_decodebin(mm_wfd_sink_t *wfd_sink)
4140 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
4141 MMWFDSinkGstElement *v_decodebin = NULL;
4142 GstObject *parent = NULL;
4145 wfd_sink_debug_fenter();
4147 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
4149 if (wfd_sink->pipeline &&
4150 wfd_sink->pipeline->v_decodebin &&
4151 wfd_sink->pipeline->v_decodebin[WFD_SINK_V_D_BIN].gst) {
4152 v_decodebin = wfd_sink->pipeline->v_decodebin;
4154 wfd_sink_debug("video decodebin is not created, nothing to destroy");
4155 return MM_ERROR_NONE;
4159 parent = gst_element_get_parent(v_decodebin[WFD_SINK_V_D_BIN].gst);
4161 wfd_sink_debug("video decodebin has no parent.. need to relase by itself");
4163 if (GST_STATE(v_decodebin[WFD_SINK_V_D_BIN].gst) >= GST_STATE_READY) {
4164 wfd_sink_debug("try to change state of video decodebin to NULL");
4165 ret = gst_element_set_state(v_decodebin[WFD_SINK_V_D_BIN].gst, GST_STATE_NULL);
4166 if (ret != GST_STATE_CHANGE_SUCCESS) {
4167 wfd_sink_error("failed to change state of video decodebin to NULL");
4168 return MM_ERROR_WFD_INTERNAL;
4171 /* release element which are not added to bin */
4172 for (i = 1; i < WFD_SINK_V_D_NUM; i++) { /* NOTE : skip bin */
4173 if (v_decodebin[i].gst) {
4174 parent = gst_element_get_parent(v_decodebin[i].gst);
4176 wfd_sink_debug("unref %s(current ref %d)",
4177 GST_STR_NULL(GST_ELEMENT_NAME(v_decodebin[i].gst)),
4178 ((GObject *) v_decodebin[i].gst)->ref_count);
4179 gst_object_unref(GST_OBJECT(v_decodebin[i].gst));
4180 v_decodebin[i].gst = NULL;
4182 wfd_sink_debug("%s has parent.(current ref %d)",
4183 GST_STR_NULL(GST_ELEMENT_NAME(v_decodebin[i].gst)),
4184 ((GObject *) v_decodebin[i].gst)->ref_count);
4185 gst_object_unref(GST_OBJECT(parent));
4190 /* release video decodebin with it's childs */
4191 if (v_decodebin[WFD_SINK_V_D_BIN].gst) {
4192 wfd_sink_debug("unref %s(current ref %d)",
4193 GST_STR_NULL(GST_ELEMENT_NAME(v_decodebin[WFD_SINK_V_D_BIN].gst)),
4194 ((GObject *)v_decodebin[WFD_SINK_V_D_BIN].gst)->ref_count);
4196 gst_object_unref(GST_OBJECT(v_decodebin[WFD_SINK_V_D_BIN].gst));
4197 v_decodebin[WFD_SINK_V_D_BIN].gst = NULL;
4200 wfd_sink_debug("video decodebin has parent(%s), unref it",
4201 GST_STR_NULL(GST_OBJECT_NAME(GST_OBJECT(parent))));
4203 gst_object_unref(GST_OBJECT(parent));
4207 wfd_sink->video_decodebin_is_linked = FALSE;
4209 MMWFDSINK_FREEIF(wfd_sink->pipeline->v_decodebin);
4211 wfd_sink_debug_fleave();
4213 return MM_ERROR_NONE;
4216 int __mm_wfd_sink_create_video_decodebin(mm_wfd_sink_t *wfd_sink)
4218 MMWFDSinkGstElement *v_decodebin = NULL;
4219 guint video_codec = WFD_VIDEO_UNKNOWN;
4220 GList *element_bucket = NULL;
4221 gboolean link = TRUE;
4224 wfd_sink_debug_fenter();
4226 wfd_sink_return_val_if_fail(wfd_sink &&
4228 MM_ERROR_WFD_NOT_INITIALIZED);
4230 if (wfd_sink->pipeline->v_decodebin) {
4231 wfd_sink_debug("video decodebin is already created... nothing to do");
4232 return MM_ERROR_NONE;
4235 /* check video decodebin could be linked now */
4236 switch (wfd_sink->stream_info.video_stream_info.codec) {
4237 case MM_WFD_SINK_VIDEO_CODEC_H264:
4238 video_codec = WFD_VIDEO_H264;
4241 case MM_WFD_SINK_VIDEO_CODEC_H265:
4242 video_codec = WFD_VIDEO_H265;
4245 case MM_WFD_SINK_VIDEO_CODEC_NONE:
4247 wfd_sink_debug("video decodebin could NOT be linked now, just create");
4248 video_codec = wfd_sink->ini.wfd_video_formats.video_codec | wfd_sink->ini.wfd2_video_formats.video_codec;
4254 v_decodebin = (MMWFDSinkGstElement *)g_malloc0(sizeof(MMWFDSinkGstElement) * WFD_SINK_V_D_NUM);
4256 wfd_sink_error("failed to allocate memory for video decodebin");
4257 return MM_ERROR_WFD_NO_FREE_SPACE;
4260 memset(v_decodebin, 0, sizeof(MMWFDSinkGstElement) * WFD_SINK_V_D_NUM);
4262 /* create video decodebin */
4263 v_decodebin[WFD_SINK_V_D_BIN].id = WFD_SINK_V_D_BIN;
4264 v_decodebin[WFD_SINK_V_D_BIN].gst = gst_bin_new("video_decodebin");
4265 if (!v_decodebin[WFD_SINK_V_D_BIN].gst) {
4266 wfd_sink_error("failed to create video decodebin");
4271 MMWFDSINK_CREATE_ELEMENT(v_decodebin, WFD_SINK_V_D_QUEUE, "queue", "video_queue", FALSE);
4272 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_QUEUE].gst, "sink");
4273 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_QUEUE].gst, "src");
4274 if (v_decodebin[WFD_SINK_V_D_QUEUE].gst)
4275 __mm_wfd_sink_prepare_queue(wfd_sink, v_decodebin[WFD_SINK_V_D_QUEUE].gst);
4278 MMWFDSINK_CREATE_ELEMENT(v_decodebin, WFD_SINK_V_D_HDCP, wfd_sink->ini.name_of_video_hdcp, "video_hdcp", FALSE);
4279 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_HDCP].gst, "sink");
4280 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_HDCP].gst, "src");
4282 if (video_codec & WFD_VIDEO_H264) {
4284 MMWFDSINK_CREATE_ELEMENT(v_decodebin, WFD_SINK_V_D_H264_PARSE, wfd_sink->ini.name_of_video_h264_parser, "video_h264_parser", FALSE);
4285 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_H264_PARSE].gst, "sink");
4286 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_H264_PARSE].gst, "src");
4289 MMWFDSINK_CREATE_ELEMENT(v_decodebin, WFD_SINK_V_D_H264_DEC, wfd_sink->ini.name_of_video_h264_decoder, "video_h264_dec", FALSE);
4290 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_H264_DEC].gst, "sink");
4291 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_H264_DEC].gst, "src");
4292 if (v_decodebin[WFD_SINK_V_D_H264_DEC].gst) {
4293 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_videodec(wfd_sink, v_decodebin[WFD_SINK_V_D_H264_DEC].gst)) {
4294 wfd_sink_error("failed to set video decoder property...");
4300 if (video_codec & WFD_VIDEO_H265) {
4302 MMWFDSINK_CREATE_ELEMENT(v_decodebin, WFD_SINK_V_D_H265_PARSE, wfd_sink->ini.name_of_video_h264_parser, "video_h265_parser", FALSE);
4303 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_H265_PARSE].gst, "sink");
4304 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_H265_PARSE].gst, "src");
4307 MMWFDSINK_CREATE_ELEMENT(v_decodebin, WFD_SINK_V_D_H265_DEC, wfd_sink->ini.name_of_video_h265_decoder, "video_h265_dec", FALSE);
4308 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_H265_DEC].gst, "sink");
4309 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_H265_DEC].gst, "src");
4310 if (v_decodebin[WFD_SINK_V_D_H265_DEC].gst) {
4311 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_videodec(wfd_sink, v_decodebin[WFD_SINK_V_D_H265_DEC].gst)) {
4312 wfd_sink_error("failed to set video decoder property...");
4318 g_list_free(element_bucket);
4321 wfd_sink->pipeline->v_decodebin = v_decodebin;
4323 /* link video decodebin if video codec is fixed */
4325 if (MM_ERROR_NONE != __mm_wfd_sink_link_video_decodebin(wfd_sink)) {
4326 wfd_sink_error("failed to link video decodebin, destroy video decodebin");
4327 __mm_wfd_sink_destroy_video_decodebin(wfd_sink);
4328 return MM_ERROR_WFD_INTERNAL;
4332 wfd_sink_debug_fleave();
4334 return MM_ERROR_NONE;
4338 wfd_sink_error("failed to create video decodebin, releasing all");
4340 g_list_free(element_bucket);
4342 /* release element which are not added to bin */
4343 for (i = 1; i < WFD_SINK_V_D_NUM; i++) { /* NOTE : skip bin */
4344 if (v_decodebin != NULL && v_decodebin[i].gst) {
4345 GstObject *parent = NULL;
4346 parent = gst_element_get_parent(v_decodebin[i].gst);
4349 gst_object_unref(GST_OBJECT(v_decodebin[i].gst));
4350 v_decodebin[i].gst = NULL;
4352 gst_object_unref(GST_OBJECT(parent));
4358 /* release video decodebin with it's childs */
4359 if (v_decodebin != NULL && v_decodebin[WFD_SINK_V_D_BIN].gst) {
4360 gst_object_unref(GST_OBJECT(v_decodebin[WFD_SINK_V_D_BIN].gst));
4361 v_decodebin[WFD_SINK_V_D_BIN].gst = NULL;
4364 MMWFDSINK_FREEIF(v_decodebin);
4366 return MM_ERROR_WFD_INTERNAL;
4369 static int __mm_wfd_sink_destroy_video_sinkbin(mm_wfd_sink_t *wfd_sink)
4371 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
4372 MMWFDSinkGstElement *v_sinkbin = NULL;
4373 GstObject *parent = NULL;
4376 wfd_sink_debug_fenter();
4378 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
4380 if (wfd_sink->pipeline &&
4381 wfd_sink->pipeline->v_sinkbin &&
4382 wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_BIN].gst) {
4383 v_sinkbin = wfd_sink->pipeline->v_sinkbin;
4385 wfd_sink_debug("video sinkbin is not created, nothing to destroy");
4386 return MM_ERROR_NONE;
4390 parent = gst_element_get_parent(v_sinkbin[WFD_SINK_V_S_BIN].gst);
4392 wfd_sink_debug("video sinkbin has no parent.. need to relase by itself");
4394 if (GST_STATE(v_sinkbin[WFD_SINK_V_S_BIN].gst) >= GST_STATE_READY) {
4395 wfd_sink_debug("try to change state of video sinkbin to NULL");
4396 ret = gst_element_set_state(v_sinkbin[WFD_SINK_V_S_BIN].gst, GST_STATE_NULL);
4397 if (ret != GST_STATE_CHANGE_SUCCESS) {
4398 wfd_sink_error("failed to change state of video sinkbin to NULL");
4399 return MM_ERROR_WFD_INTERNAL;
4402 /* release element which are not added to bin */
4403 for (i = 1; i < WFD_SINK_V_S_NUM; i++) { /* NOTE : skip bin */
4404 if (v_sinkbin[i].gst) {
4405 parent = gst_element_get_parent(v_sinkbin[i].gst);
4407 wfd_sink_debug("unref %s(current ref %d)",
4408 GST_STR_NULL(GST_ELEMENT_NAME(v_sinkbin[i].gst)),
4409 ((GObject *) v_sinkbin[i].gst)->ref_count);
4410 gst_object_unref(GST_OBJECT(v_sinkbin[i].gst));
4411 v_sinkbin[i].gst = NULL;
4413 wfd_sink_debug("%s has parent.(current ref %d)",
4414 GST_STR_NULL(GST_ELEMENT_NAME(v_sinkbin[i].gst)),
4415 ((GObject *) v_sinkbin[i].gst)->ref_count);
4416 gst_object_unref(GST_OBJECT(parent));
4421 /* release video sinkbin with it's childs */
4422 if (v_sinkbin[WFD_SINK_V_S_BIN].gst) {
4423 gst_object_unref(GST_OBJECT(v_sinkbin[WFD_SINK_V_S_BIN].gst));
4424 v_sinkbin[WFD_SINK_V_S_BIN].gst = NULL;
4427 wfd_sink_debug("video sinkbin has parent(%s), unref it ",
4428 GST_STR_NULL(GST_OBJECT_NAME(GST_OBJECT(parent))));
4430 gst_object_unref(GST_OBJECT(parent));
4434 MMWFDSINK_FREEIF(wfd_sink->pipeline->v_sinkbin);
4436 wfd_sink_debug_fleave();
4438 return MM_ERROR_NONE;
4441 int __mm_wfd_sink_create_video_sinkbin(mm_wfd_sink_t *wfd_sink)
4443 MMWFDSinkGstElement *first_element = NULL;
4444 MMWFDSinkGstElement *v_sinkbin = NULL;
4445 GList *element_bucket = NULL;
4447 GstPad *ghostpad = NULL;
4449 gint surface_type = MM_DISPLAY_SURFACE_OVERLAY;
4451 wfd_sink_debug_fenter();
4453 wfd_sink_return_val_if_fail(wfd_sink &&
4455 MM_ERROR_WFD_NOT_INITIALIZED);
4457 if (wfd_sink->pipeline->v_sinkbin != NULL) {
4458 wfd_sink_error("The video sink bin is already created.");
4459 return MM_ERROR_NONE;
4463 v_sinkbin = (MMWFDSinkGstElement *)g_malloc0(sizeof(MMWFDSinkGstElement) * WFD_SINK_V_S_NUM);
4465 wfd_sink_error("failed to allocate memory for video sinkbin");
4466 return MM_ERROR_WFD_NO_FREE_SPACE;
4469 memset(v_sinkbin, 0, sizeof(MMWFDSinkGstElement) * WFD_SINK_V_S_NUM);
4471 /* create video sinkbin */
4472 v_sinkbin[WFD_SINK_V_S_BIN].id = WFD_SINK_V_S_BIN;
4473 v_sinkbin[WFD_SINK_V_S_BIN].gst = gst_bin_new("video_sinkbin");
4474 if (!v_sinkbin[WFD_SINK_V_S_BIN].gst) {
4475 wfd_sink_error("failed to create video sinkbin");
4479 /* create convert */
4480 MMWFDSINK_CREATE_ELEMENT(v_sinkbin, WFD_SINK_V_S_CONVERT, wfd_sink->ini.name_of_video_converter, "video_convert", TRUE);
4481 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_sinkbin[WFD_SINK_V_S_CONVERT].gst, "sink");
4482 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_sinkbin[WFD_SINK_V_S_CONVERT].gst, "src");
4485 MMWFDSINK_CREATE_ELEMENT(v_sinkbin, WFD_SINK_V_S_FILTER, wfd_sink->ini.name_of_video_filter, "video_filter", TRUE);
4486 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_sinkbin[WFD_SINK_V_S_FILTER].gst, "sink");
4487 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_sinkbin[WFD_SINK_V_S_FILTER].gst, "src");
4488 if (v_sinkbin[WFD_SINK_V_S_FILTER].gst) {
4489 GstCaps *caps = NULL;
4490 caps = gst_caps_new_simple("video/x-raw", "format", G_TYPE_STRING, "SN12", NULL);
4491 g_object_set(G_OBJECT(v_sinkbin[WFD_SINK_V_S_FILTER].gst), "caps", caps, NULL);
4492 gst_object_unref(GST_OBJECT(caps));
4497 mm_attrs_get_int_by_name(wfd_sink->attrs, "display_surface_type", &surface_type);
4499 if (surface_type == MM_DISPLAY_SURFACE_OVERLAY) {
4500 MMWFDSINK_CREATE_ELEMENT(v_sinkbin, WFD_SINK_V_S_SINK, wfd_sink->ini.name_of_video_sink, "video_sink", TRUE);
4501 } else if (surface_type == MM_DISPLAY_SURFACE_EVAS) {
4502 MMWFDSINK_CREATE_ELEMENT(v_sinkbin, WFD_SINK_V_S_SINK, wfd_sink->ini.name_of_video_evas_sink, "video_sink", TRUE);
4504 wfd_sink_error("failed to set video sink....");
4508 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_sinkbin[WFD_SINK_V_S_SINK].gst, "sink");
4509 if (v_sinkbin[WFD_SINK_V_S_SINK].gst) {
4510 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_videosink(wfd_sink, v_sinkbin[WFD_SINK_V_S_SINK].gst)) {
4511 wfd_sink_error("failed to set video sink property....");
4516 /* adding created elements to video sinkbin */
4517 if (!__mm_wfd_sink_gst_element_add_bucket_to_bin(GST_BIN_CAST(v_sinkbin[WFD_SINK_V_S_BIN].gst), element_bucket, FALSE)) {
4518 wfd_sink_error("failed to add elements to video sinkbin");
4522 /* linking elements in the bucket by added order. */
4523 if (__mm_wfd_sink_gst_element_link_bucket(element_bucket) == -1) {
4524 wfd_sink_error("failed to link elements of the video sinkbin");
4528 /* get first element's sinkpad for creating ghostpad */
4529 first_element = (MMWFDSinkGstElement *)g_list_nth_data(element_bucket, 0);
4530 if (!first_element) {
4531 wfd_sink_error("failed to get first element of the video sinkbin");
4535 pad = gst_element_get_static_pad(GST_ELEMENT(first_element->gst), "sink");
4537 wfd_sink_error("failed to get pad from first element(%s) of the video sinkbin",
4538 GST_STR_NULL(GST_ELEMENT_NAME(first_element->gst)));
4542 ghostpad = gst_ghost_pad_new("sink", pad);
4544 wfd_sink_error("failed to create ghostpad of the video sinkbin");
4548 if (FALSE == gst_element_add_pad(v_sinkbin[WFD_SINK_V_S_BIN].gst, ghostpad)) {
4549 wfd_sink_error("failed to add ghostpad to video sinkbin");
4553 gst_object_unref(GST_OBJECT(pad));
4556 g_list_free(element_bucket);
4557 element_bucket = NULL;
4560 wfd_sink->pipeline->v_sinkbin = v_sinkbin;
4562 wfd_sink_debug_fleave();
4564 return MM_ERROR_NONE;
4568 wfd_sink_error("failed to create video sinkbin, releasing all");
4571 gst_object_unref(GST_OBJECT(pad));
4576 gst_object_unref(GST_OBJECT(ghostpad));
4580 if (element_bucket) {
4581 g_list_free(element_bucket);
4582 element_bucket = NULL;
4585 /* release element which are not added to bin */
4586 for (i = 1; i < WFD_SINK_V_S_NUM; i++) { /* NOTE : skip bin */
4587 if (v_sinkbin != NULL && v_sinkbin[i].gst) {
4588 GstObject *parent = NULL;
4589 parent = gst_element_get_parent(v_sinkbin[i].gst);
4592 gst_object_unref(GST_OBJECT(v_sinkbin[i].gst));
4593 v_sinkbin[i].gst = NULL;
4595 gst_object_unref(GST_OBJECT(parent));
4601 /* release video sinkbin with it's childs */
4602 if (v_sinkbin != NULL && v_sinkbin[WFD_SINK_V_S_BIN].gst) {
4603 gst_object_unref(GST_OBJECT(v_sinkbin[WFD_SINK_V_S_BIN].gst));
4604 v_sinkbin[WFD_SINK_V_S_BIN].gst = NULL;
4606 MMWFDSINK_FREEIF(v_sinkbin);
4608 return MM_ERROR_WFD_INTERNAL;
4611 static int __mm_wfd_sink_destroy_pipeline(mm_wfd_sink_t *wfd_sink)
4613 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
4616 wfd_sink_debug_fenter();
4618 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
4620 /* cleanup gst stuffs */
4621 if (wfd_sink->pipeline) {
4622 MMWFDSinkGstElement *mainbin = wfd_sink->pipeline->mainbin;
4626 PRINT_WFD_REF_COUNT(wfd_sink);
4628 ret = gst_element_set_state(mainbin[WFD_SINK_M_PIPE].gst, GST_STATE_NULL);
4629 if (ret != GST_STATE_CHANGE_SUCCESS) {
4630 wfd_sink_error("failed to change state of pipeline to NULL");
4631 return MM_ERROR_WFD_INTERNAL;
4633 wfd_sink_debug("Successed to change state of pipeline to NULL");
4636 bus = gst_pipeline_get_bus(GST_PIPELINE(mainbin[WFD_SINK_M_PIPE].gst));
4638 GstMessage *gst_msg = NULL;
4639 while ((gst_msg = gst_bus_pop(bus)) != NULL) {
4640 _mm_wfd_sink_msg_callback(bus, gst_msg, (gpointer)wfd_sink);
4641 gst_message_unref(gst_msg);
4644 gst_object_unref(bus);
4648 PRINT_WFD_REF_COUNT(wfd_sink);
4650 if (MM_ERROR_NONE != __mm_wfd_sink_destroy_video_decodebin(wfd_sink)) {
4651 wfd_sink_error("failed to destroy video decodebin");
4652 return MM_ERROR_WFD_INTERNAL;
4655 if (MM_ERROR_NONE != __mm_wfd_sink_destroy_audio_decodebin(wfd_sink)) {
4656 wfd_sink_error("failed to destroy audio decodebin");
4657 return MM_ERROR_WFD_INTERNAL;
4660 if (MM_ERROR_NONE != __mm_wfd_sink_destroy_video_sinkbin(wfd_sink)) {
4661 wfd_sink_error("failed to destroy video sinkbin");
4662 return MM_ERROR_WFD_INTERNAL;
4665 if (MM_ERROR_NONE != __mm_wfd_sink_destroy_audio_sinkbin(wfd_sink)) {
4666 wfd_sink_error("failed to destroy audio sinkbin");
4667 return MM_ERROR_WFD_INTERNAL;
4670 gst_object_unref(GST_OBJECT(mainbin[WFD_SINK_M_PIPE].gst));
4671 mainbin[WFD_SINK_M_PIPE].gst = NULL;
4673 MMWFDSINK_FREEIF(mainbin);
4676 MMWFDSINK_FREEIF(wfd_sink->pipeline);
4679 if (wfd_sink->msg_callback_id > 0) {
4680 g_source_remove(wfd_sink->msg_callback_id);
4681 wfd_sink->msg_callback_id = 0;
4684 wfd_sink->audio_decodebin_is_linked = FALSE;
4685 wfd_sink->video_decodebin_is_linked = FALSE;
4686 wfd_sink->need_to_reset_basetime = FALSE;
4688 wfd_sink_debug_fleave();
4690 return MM_ERROR_NONE;
4694 __mm_wfd_sink_dump_pipeline_state(mm_wfd_sink_t *wfd_sink)
4696 GstIterator *iter = NULL;
4697 gboolean done = FALSE;
4699 GstElement *item = NULL;
4700 GstElementFactory *factory = NULL;
4702 GstState state = GST_STATE_VOID_PENDING;
4703 GstState pending = GST_STATE_VOID_PENDING;
4704 GstClockTime time = 200 * GST_MSECOND;
4706 wfd_sink_debug_fenter();
4708 wfd_sink_return_if_fail(wfd_sink &&
4709 wfd_sink->pipeline &&
4710 wfd_sink->pipeline->mainbin &&
4711 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst);
4713 iter = gst_bin_iterate_recurse(GST_BIN(wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst));
4717 switch (gst_iterator_next(iter, (gpointer)&item)) {
4718 case GST_ITERATOR_OK:
4719 gst_element_get_state(GST_ELEMENT(item), &state, &pending, time);
4721 factory = gst_element_get_factory(item) ;
4723 wfd_sink_error("%s:%s : From:%s To:%s refcount : %d",
4724 GST_STR_NULL(GST_OBJECT_NAME(factory)),
4725 GST_STR_NULL(GST_ELEMENT_NAME(item)),
4726 gst_element_state_get_name(state),
4727 gst_element_state_get_name(pending),
4728 GST_OBJECT_REFCOUNT_VALUE(item));
4730 gst_object_unref(item);
4733 case GST_ITERATOR_RESYNC:
4734 gst_iterator_resync(iter);
4736 case GST_ITERATOR_ERROR:
4739 case GST_ITERATOR_DONE:
4749 item = GST_ELEMENT_CAST(wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst);
4751 gst_element_get_state(GST_ELEMENT(item), &state, &pending, time);
4753 factory = gst_element_get_factory(item) ;
4755 wfd_sink_error("%s:%s : From:%s To:%s refcount : %d",
4756 GST_OBJECT_NAME(factory),
4757 GST_ELEMENT_NAME(item),
4758 gst_element_state_get_name(state),
4759 gst_element_state_get_name(pending),
4760 GST_OBJECT_REFCOUNT_VALUE(item));
4764 gst_iterator_free(iter);
4766 wfd_sink_debug_fleave();
4772 _mm_wfds_sink_get_state_name(MMWFDSinkStateType state)
4775 case MM_WFD_SINK_STATE_NONE:
4777 case MM_WFD_SINK_STATE_NULL:
4779 case MM_WFD_SINK_STATE_PREPARED:
4781 case MM_WFD_SINK_STATE_CONNECTED:
4783 case MM_WFD_SINK_STATE_PLAYING:
4785 case MM_WFD_SINK_STATE_PAUSED:
4787 case MM_WFD_SINK_STATE_DISCONNECTED:
4788 return "DISCONNECTED";
4794 int _mm_wfd_sink_set_resolution(mm_wfd_sink_t *wfd_sink, MMWFDSinkResolution resolution)
4796 MMWFDSinkStateType cur_state = MM_WFD_SINK_STATE_NONE;
4798 wfd_sink_debug_fenter();
4800 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
4802 MMWFDSINK_PRINT_STATE(wfd_sink);
4803 cur_state = MMWFDSINK_CURRENT_STATE(wfd_sink);
4804 if (cur_state != MM_WFD_SINK_STATE_NULL) {
4805 wfd_sink_error("This function must be called when MM_WFD_SINK_STATE_NULL");
4806 return MM_ERROR_WFD_INVALID_STATE;
4809 wfd_sink->supportive_resolution = resolution;
4811 wfd_sink_debug_fleave();
4813 return MM_ERROR_NONE;
4816 void __mm_wfd_sink_print_ref_count(mm_wfd_sink_t *wfd_sink)
4819 MMWFDSinkGstElement *mainbin = NULL;
4820 MMWFDSinkGstElement *v_decodebin = NULL;
4821 MMWFDSinkGstElement *a_decodebin = NULL;
4822 MMWFDSinkGstElement *v_sinkbin = NULL;
4823 MMWFDSinkGstElement *a_sinkbin = NULL;
4825 wfd_sink_debug_fenter();
4827 wfd_sink_return_if_fail(wfd_sink);
4828 wfd_sink_return_if_fail(wfd_sink->pipeline);
4830 wfd_sink_debug("************* wfd pipeline ref count start *************");
4831 wfd_sink_debug("try to check mainbin");
4833 if (wfd_sink->pipeline->mainbin &&
4834 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst) {
4835 mainbin = wfd_sink->pipeline->mainbin;
4837 for (i = 0; i < WFD_SINK_M_NUM; i++) {
4838 if (mainbin[i].gst) {
4839 wfd_sink_debug("%s(current ref %d)",
4840 GST_ELEMENT_NAME(mainbin[i].gst),
4841 GST_OBJECT_REFCOUNT(mainbin[i].gst));
4846 wfd_sink_debug("try to check a_decodebin");
4848 if (wfd_sink->pipeline->a_decodebin &&
4849 wfd_sink->pipeline->a_decodebin[WFD_SINK_A_D_BIN].gst) {
4850 a_decodebin = wfd_sink->pipeline->a_decodebin;
4852 for (i = 0; i < WFD_SINK_A_D_NUM; i++) {
4853 if (a_decodebin[i].gst) {
4854 wfd_sink_debug("%s(current ref %d)",
4855 GST_ELEMENT_NAME(a_decodebin[i].gst),
4856 GST_OBJECT_REFCOUNT(a_decodebin[i].gst));
4861 wfd_sink_debug("try to check a_sinkbin");
4863 if (wfd_sink->pipeline->a_sinkbin &&
4864 wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_BIN].gst) {
4865 a_sinkbin = wfd_sink->pipeline->a_sinkbin;
4867 for (i = 0; i < WFD_SINK_A_S_NUM; i++) {
4868 if (a_sinkbin[i].gst) {
4869 wfd_sink_debug("%s(current ref %d)",
4870 GST_ELEMENT_NAME(a_sinkbin[i].gst),
4871 GST_OBJECT_REFCOUNT(a_sinkbin[i].gst));
4876 wfd_sink_debug("try to check v_decodebin");
4878 if (wfd_sink->pipeline->v_decodebin &&
4879 wfd_sink->pipeline->v_decodebin[WFD_SINK_V_D_BIN].gst) {
4880 v_decodebin = wfd_sink->pipeline->v_decodebin;
4882 for (i = 0; i < WFD_SINK_V_D_NUM; i++) {
4883 if (v_decodebin[i].gst) {
4884 wfd_sink_debug("%s(current ref %d)",
4885 GST_ELEMENT_NAME(v_decodebin[i].gst),
4886 GST_OBJECT_REFCOUNT(v_decodebin[i].gst));
4891 wfd_sink_debug("try to check v_sinkbin");
4893 if (wfd_sink->pipeline->v_sinkbin &&
4894 wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_BIN].gst) {
4895 v_sinkbin = wfd_sink->pipeline->v_sinkbin;
4897 for (i = 0; i < WFD_SINK_V_S_NUM; i++) {
4898 if (v_sinkbin[i].gst) {
4899 wfd_sink_debug("%s(current ref %d)",
4900 GST_ELEMENT_NAME(v_sinkbin[i].gst),
4901 GST_OBJECT_REFCOUNT(v_sinkbin[i].gst));
4905 wfd_sink_debug("************* wfd pipeline ref count end *************");
4906 wfd_sink_debug_fleave();