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 if (gst_structure_has_field(str, "video_format")) {
2642 is_valid_video_format = TRUE;
2643 video_format = g_strdup(gst_structure_get_string(str, "video_format"));
2644 if (g_strrstr(video_format, "H264")) {
2645 stream_info->video_stream_info.codec = MM_WFD_SINK_VIDEO_CODEC_H264;
2646 } else if (g_strrstr(video_format, "H265")) {
2647 stream_info->video_stream_info.codec = MM_WFD_SINK_VIDEO_CODEC_H265;
2649 wfd_sink_error("invalid video format(%s)...", video_format);
2650 is_valid_video_format = FALSE;
2653 if (is_valid_video_format == TRUE) {
2654 if (gst_structure_has_field(str, "video_width"))
2655 gst_structure_get_int(str, "video_width", &stream_info->video_stream_info.width);
2656 if (gst_structure_has_field(str, "video_height"))
2657 gst_structure_get_int(str, "video_height", &stream_info->video_stream_info.height);
2658 if (gst_structure_has_field(str, "video_framerate"))
2659 gst_structure_get_int(str, "video_framerate", &stream_info->video_stream_info.frame_rate);
2661 WFD_SINK_MANAGER_APPEND_CMD(wfd_sink, WFD_SINK_MANAGER_CMD_PREPARE_V_PIPELINE);
2663 wfd_sink_debug("video_format : %s \n \t width : %d \n \t height : %d \n \t frame_rate : %d \n \t",
2665 stream_info->video_stream_info.width,
2666 stream_info->video_stream_info.height,
2667 stream_info->video_stream_info.frame_rate);
2671 WFD_SINK_MANAGER_SIGNAL_CMD(wfd_sink);
2673 wfd_sink_debug_fleave();
2676 static void __mm_wfd_sink_prepare_video_resolution(gint resolution, guint64 *CEA_resolution,
2677 guint64 *VESA_resolution, guint64 *HH_resolution)
2679 if (resolution == MM_WFD_SINK_RESOLUTION_UNKNOWN) return;
2681 *CEA_resolution = 0;
2682 *VESA_resolution = 0;
2685 if (resolution & MM_WFD_SINK_RESOLUTION_1920x1080_P30)
2686 *CEA_resolution |= WFD_CEA_1920x1080P30;
2688 if (resolution & MM_WFD_SINK_RESOLUTION_1280x720_P30)
2689 *CEA_resolution |= WFD_CEA_1280x720P30;
2691 if (resolution & MM_WFD_SINK_RESOLUTION_960x540_P30)
2692 *HH_resolution |= WFD_HH_960x540P30;
2694 if (resolution & MM_WFD_SINK_RESOLUTION_864x480_P30)
2695 *HH_resolution |= WFD_HH_864x480P30;
2697 if (resolution & MM_WFD_SINK_RESOLUTION_720x480_P60)
2698 *CEA_resolution |= WFD_CEA_720x480P60;
2700 if (resolution & MM_WFD_SINK_RESOLUTION_640x480_P60)
2701 *CEA_resolution |= WFD_CEA_640x480P60;
2703 if (resolution & MM_WFD_SINK_RESOLUTION_640x360_P30)
2704 *HH_resolution |= WFD_HH_640x360P30;
2707 static int __mm_wfd_sink_prepare_source(mm_wfd_sink_t *wfd_sink, GstElement *wfdsrc)
2709 GstStructure *wfd_audio_codecs = NULL;
2710 GstStructure *wfd_video_formats = NULL;
2711 GstStructure *wfd_content_protection = NULL;
2712 GstStructure *wfd2_video_formats = NULL;
2713 GstStructure *wfd2_audio_codecs = NULL;
2714 gint hdcp_version = 0;
2716 guint64 CEA_resolution = 0;
2717 guint64 VESA_resolution = 0;
2718 guint64 HH_resolution = 0;
2719 GObjectClass *klass;
2721 wfd_sink_debug_fenter();
2723 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
2724 wfd_sink_return_val_if_fail(wfd_sink->attrs, MM_ERROR_WFD_NOT_INITIALIZED);
2725 wfd_sink_return_val_if_fail(wfdsrc, MM_ERROR_WFD_NOT_INITIALIZED);
2727 klass = G_OBJECT_GET_CLASS(G_OBJECT(wfdsrc));
2729 if (g_object_class_find_property(klass, "enable-pad-probe")) /* for common wfdsrc */
2730 g_object_set(G_OBJECT(wfdsrc), "enable-pad-probe", wfd_sink->ini.trace_buffers_of_wfdsrc, NULL);
2731 g_object_set(G_OBJECT(wfdsrc), "udp-buffer-size", 2097152, NULL);
2732 g_object_set(G_OBJECT(wfdsrc), "latency", wfd_sink->ini.jitter_buffer_latency, NULL);
2733 g_object_set(G_OBJECT(wfdsrc), "user-agent", wfd_sink->ini.user_agent, NULL);
2734 g_object_set(G_OBJECT(wfdsrc), "dump-rtsp-message", wfd_sink->ini.dump_rtsp_message, NULL);
2735 if (g_object_class_find_property(klass, "dump-rtp-data"))
2736 g_object_set(G_OBJECT(wfdsrc), "dump-rtp-data", wfd_sink->ini.dump_rtp_data, NULL);
2737 if (g_object_class_find_property(klass, "trace-first-buffer"))
2738 g_object_set(G_OBJECT(wfdsrc), "trace-first-buffer", wfd_sink->ini.trace_first_buffer, NULL);
2739 if (g_object_class_find_property(klass, "trace-buffers"))
2740 g_object_set(G_OBJECT(wfdsrc), "trace-buffers", wfd_sink->ini.trace_buffers, NULL);
2741 if (g_object_class_find_property(klass, "do-request"))
2742 g_object_set(G_OBJECT(wfdsrc), "do-request", wfd_sink->ini.enable_retransmission, NULL);
2744 /* set audio parameter for Wi-Fi Display session negotiation */
2745 wfd_audio_codecs = gst_structure_new("wfd_audio_codecs",
2746 "audio_codec", G_TYPE_UINT, wfd_sink->ini.wfd_audio_codecs.audio_codec,
2747 "audio_latency", G_TYPE_UINT, wfd_sink->ini.wfd_audio_codecs.audio_latency,
2748 "audio_channels", G_TYPE_UINT, wfd_sink->ini.wfd_audio_codecs.audio_channel,
2749 "audio_sampling_frequency", G_TYPE_UINT, wfd_sink->ini.wfd_audio_codecs.audio_sampling_frequency,
2752 if (wfd_audio_codecs) {
2753 g_object_set(G_OBJECT(wfdsrc), "wfd-audio-codecs", wfd_audio_codecs, NULL);
2754 gst_structure_free(wfd_audio_codecs);
2755 wfd_audio_codecs = NULL;
2758 /* set video parameter for Wi-Fi Display session negotiation */
2759 CEA_resolution = wfd_sink->ini.wfd_video_formats.video_cea_support;
2760 VESA_resolution = wfd_sink->ini.wfd_video_formats.video_vesa_support;
2761 HH_resolution = wfd_sink->ini.wfd_video_formats.video_hh_support;
2763 __mm_wfd_sink_prepare_video_resolution(wfd_sink->supportive_resolution,
2764 &CEA_resolution, &VESA_resolution, &HH_resolution);
2765 wfd_video_formats = gst_structure_new("wfd_video_formats",
2766 "video_codec", G_TYPE_UINT, wfd_sink->ini.wfd_video_formats.video_codec,
2767 "video_native_resolution", G_TYPE_UINT, wfd_sink->ini.wfd_video_formats.video_native_resolution,
2768 "video_cea_support", G_TYPE_UINT64, CEA_resolution,
2769 "video_vesa_support", G_TYPE_UINT64, VESA_resolution,
2770 "video_hh_support", G_TYPE_UINT64, HH_resolution,
2771 "video_profile", G_TYPE_UINT, wfd_sink->ini.wfd_video_formats.video_profile,
2772 "video_level", G_TYPE_UINT, wfd_sink->ini.wfd_video_formats.video_level,
2773 "video_latency", G_TYPE_UINT, wfd_sink->ini.wfd_video_formats.video_latency,
2774 "video_vertical_resolution", G_TYPE_INT, wfd_sink->ini.wfd_video_formats.video_vertical_resolution,
2775 "video_horizontal_resolution", G_TYPE_INT, wfd_sink->ini.wfd_video_formats.video_horizontal_resolution,
2776 "video_minimum_slicing", G_TYPE_INT, wfd_sink->ini.wfd_video_formats.video_minimum_slicing,
2777 "video_slice_enc_param", G_TYPE_INT, wfd_sink->ini.wfd_video_formats.video_slice_enc_param,
2778 "video_framerate_control_support", G_TYPE_INT, wfd_sink->ini.wfd_video_formats.video_framerate_control_support,
2781 if (wfd_video_formats) {
2782 g_object_set(G_OBJECT(wfdsrc), "wfd-video-formats", wfd_video_formats, NULL);
2783 gst_structure_free(wfd_video_formats);
2784 wfd_video_formats = NULL;
2788 /* set hdcp parameter for Wi-Fi Display session negotiation */
2789 if (wfd_sink->ini.wfd_content_protection.enable_hdcp) {
2790 mm_attrs_get_int_by_name(wfd_sink->attrs, "hdcp_version", &hdcp_version);
2791 mm_attrs_get_int_by_name(wfd_sink->attrs, "hdcp_port", &hdcp_port);
2792 wfd_sink_debug("set hdcp version %d with %d port", hdcp_version, hdcp_port);
2794 wfd_content_protection = gst_structure_new("wfd_content_protection",
2795 "hdcp_version", G_TYPE_INT, hdcp_version,
2796 "hdcp_port_no", G_TYPE_INT, hdcp_port,
2799 if (wfd_content_protection) {
2800 g_object_set(G_OBJECT(wfdsrc), "wfd-content-protection", wfd_content_protection, NULL);
2801 gst_structure_free(wfd_content_protection);
2802 wfd_content_protection = NULL;
2806 if (g_object_class_find_property(klass, "wfd2-audio-codecs")) {
2807 /* set audio parameter for Wi-Fi Display R2 session negotiation */
2808 wfd2_audio_codecs = gst_structure_new("wfd2-audio-codecs",
2809 "audio_codec", G_TYPE_UINT, wfd_sink->ini.wfd2_audio_codecs.audio_codec,
2810 "audio_lpcm_mode", G_TYPE_UINT, wfd_sink->ini.wfd2_audio_codecs.audio_lpcm_mode,
2811 "audio_aac_mode", G_TYPE_UINT, wfd_sink->ini.wfd2_audio_codecs.audio_aac_mode,
2812 "audio_ac3_mode", G_TYPE_UINT, wfd_sink->ini.wfd2_audio_codecs.audio_ac3_mode,
2815 if (wfd2_audio_codecs) {
2816 g_object_set(G_OBJECT(wfdsrc), "wfd2-audio-codecs", wfd2_audio_codecs, NULL);
2817 gst_structure_free(wfd2_audio_codecs);
2818 wfd2_audio_codecs = NULL;
2822 if (g_object_class_find_property(klass, "wfd2-video-format-h264")) {
2823 /* set video parameter for Wi-Fi Display R2 session negotiation */
2824 CEA_resolution = wfd_sink->ini.wfd2_video_h264_info.video_cea_support;
2825 VESA_resolution = wfd_sink->ini.wfd2_video_h264_info.video_vesa_support;
2826 HH_resolution = wfd_sink->ini.wfd2_video_h264_info.video_hh_support;
2828 if (wfd_sink->ini.wfd2_video_formats.video_codec & WFD_VIDEO_H264) {
2829 wfd2_video_formats = gst_structure_new("wfd2-video-format-h264",
2830 "video_codec", G_TYPE_UINT, WFD_VIDEO_H264,
2831 "video_native_resolution", G_TYPE_UINT, wfd_sink->ini.wfd2_video_formats.video_native_resolution,
2832 "video_cea_support", G_TYPE_UINT64, CEA_resolution,
2833 "video_vesa_support", G_TYPE_UINT64, VESA_resolution,
2834 "video_hh_support", G_TYPE_UINT64, HH_resolution,
2835 "video_profile", G_TYPE_UINT, wfd_sink->ini.wfd2_video_h264_info.video_profile,
2836 "video_level", G_TYPE_UINT, wfd_sink->ini.wfd2_video_h264_info.video_level,
2837 "video_latency", G_TYPE_UINT, wfd_sink->ini.wfd2_video_h264_info.video_latency,
2838 "video_vertical_resolution", G_TYPE_INT, wfd_sink->ini.wfd2_video_h264_info.video_vertical_resolution,
2839 "video_horizontal_resolution", G_TYPE_INT, wfd_sink->ini.wfd2_video_h264_info.video_horizontal_resolution,
2840 "video_minimum_slicing", G_TYPE_INT, wfd_sink->ini.wfd2_video_h264_info.video_minimum_slicing,
2841 "video_slice_enc_param", G_TYPE_INT, wfd_sink->ini.wfd2_video_h264_info.video_slice_enc_param,
2842 "video_framerate_control_support", G_TYPE_INT, wfd_sink->ini.wfd2_video_h264_info.video_framerate_control_support,
2843 "video_non_transcoding_support", G_TYPE_INT, wfd_sink->ini.wfd2_video_formats.video_non_transcoding_support,
2846 if (wfd2_video_formats) {
2847 g_object_set(G_OBJECT(wfdsrc), "wfd2-video-format-h264", wfd2_video_formats, NULL);
2848 gst_structure_free(wfd2_video_formats);
2849 wfd2_video_formats = NULL;
2854 if (g_object_class_find_property(klass, "wfd2-video-format-h265")) {
2855 /* set video parameter for Wi-Fi Display R2 session negotiation */
2856 CEA_resolution = wfd_sink->ini.wfd2_video_h265_info.video_cea_support;
2857 VESA_resolution = wfd_sink->ini.wfd2_video_h265_info.video_vesa_support;
2858 HH_resolution = wfd_sink->ini.wfd2_video_h265_info.video_hh_support;
2860 if (wfd_sink->ini.wfd2_video_formats.video_codec & WFD_VIDEO_H265) {
2861 wfd2_video_formats = gst_structure_new("wfd2-video-format-h265",
2862 "video_codec", G_TYPE_UINT, WFD_VIDEO_H265,
2863 "video_native_resolution", G_TYPE_UINT, wfd_sink->ini.wfd2_video_formats.video_native_resolution,
2864 "video_cea_support", G_TYPE_UINT64, CEA_resolution,
2865 "video_vesa_support", G_TYPE_UINT64, VESA_resolution,
2866 "video_hh_support", G_TYPE_UINT64, HH_resolution,
2867 "video_profile", G_TYPE_UINT, wfd_sink->ini.wfd2_video_h265_info.video_profile,
2868 "video_level", G_TYPE_UINT, wfd_sink->ini.wfd2_video_h265_info.video_level,
2869 "video_latency", G_TYPE_UINT, wfd_sink->ini.wfd2_video_h265_info.video_latency,
2870 "video_vertical_resolution", G_TYPE_INT, wfd_sink->ini.wfd2_video_h265_info.video_vertical_resolution,
2871 "video_horizontal_resolution", G_TYPE_INT, wfd_sink->ini.wfd2_video_h265_info.video_horizontal_resolution,
2872 "video_minimum_slicing", G_TYPE_INT, wfd_sink->ini.wfd2_video_h265_info.video_minimum_slicing,
2873 "video_slice_enc_param", G_TYPE_INT, wfd_sink->ini.wfd2_video_h265_info.video_slice_enc_param,
2874 "video_framerate_control_support", G_TYPE_INT, wfd_sink->ini.wfd2_video_h265_info.video_framerate_control_support,
2875 "video_non_transcoding_support", G_TYPE_INT, wfd_sink->ini.wfd2_video_formats.video_non_transcoding_support,
2878 if (wfd2_video_formats) {
2879 g_object_set(G_OBJECT(wfdsrc), "wfd2-video-format-h265", wfd2_video_formats, NULL);
2880 gst_structure_free(wfd2_video_formats);
2881 wfd2_video_formats = NULL;
2886 wfd_sink->update_stream_info_sig_id = g_signal_connect(wfdsrc, "update-media-info",
2887 G_CALLBACK(__mm_wfd_sink_update_stream_info), wfd_sink);
2889 wfd_sink->change_av_format_sig_id = g_signal_connect(wfdsrc, "change-av-format",
2890 G_CALLBACK(__mm_wfd_sink_change_av_format), wfd_sink);
2892 wfd_sink_debug_fleave();
2894 return MM_ERROR_NONE;
2897 static int __mm_wfd_sink_prepare_demux(mm_wfd_sink_t *wfd_sink, GstElement *demux)
2899 wfd_sink_debug_fenter();
2901 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
2902 wfd_sink_return_val_if_fail(demux, MM_ERROR_WFD_NOT_INITIALIZED);
2904 g_signal_connect(demux, "pad-added",
2905 G_CALLBACK(__mm_wfd_sink_demux_pad_added), wfd_sink);
2907 wfd_sink_debug_fleave();
2909 return MM_ERROR_NONE;
2912 static void __mm_wfd_sink_queue_overrun(GstElement *queue, gpointer u_data)
2916 wfd_sink_debug_fenter();
2918 return_if_fail(queue);
2920 g_object_get(G_OBJECT(queue), "current-level-time", &time, NULL);
2922 wfd_sink_warning("%s is overrun(%" GST_TIME_FORMAT")",
2923 GST_ELEMENT_NAME(queue), GST_TIME_ARGS(time));
2925 wfd_sink_debug_fleave();
2930 static void __mm_wfd_sink_prepare_queue(mm_wfd_sink_t *wfd_sink, GstElement *queue)
2932 wfd_sink_debug_fenter();
2934 wfd_sink_return_if_fail(wfd_sink);
2935 wfd_sink_return_if_fail(queue);
2937 /* set maximum buffer size of queue as 3sec */
2938 g_object_set(G_OBJECT(queue), "max-size-bytes", 0, NULL);
2939 g_object_set(G_OBJECT(queue), "max-size-buffers", 0, NULL);
2940 g_object_set(G_OBJECT(queue), "max-size-time", (guint64)3000000000ULL, NULL);
2941 g_signal_connect(queue, "overrun",
2942 G_CALLBACK(__mm_wfd_sink_queue_overrun), wfd_sink);
2944 wfd_sink_debug_fleave();
2950 static int __mm_wfd_sink_create_pipeline(mm_wfd_sink_t *wfd_sink)
2952 MMWFDSinkGstElement *mainbin = NULL;
2953 GList *element_bucket = NULL;
2957 wfd_sink_debug_fenter();
2959 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
2960 wfd_sink_return_val_if_fail(wfd_sink->attrs, MM_ERROR_WFD_NOT_INITIALIZED);
2962 /* Create pipeline */
2963 wfd_sink->pipeline = (MMWFDSinkGstPipelineInfo *) g_malloc0(sizeof(MMWFDSinkGstPipelineInfo));
2964 if (wfd_sink->pipeline == NULL)
2967 memset(wfd_sink->pipeline, 0, sizeof(MMWFDSinkGstPipelineInfo));
2969 /* create mainbin */
2970 mainbin = (MMWFDSinkGstElement *) g_malloc0(sizeof(MMWFDSinkGstElement) * WFD_SINK_M_NUM);
2971 if (mainbin == NULL)
2974 memset(mainbin, 0, sizeof(MMWFDSinkGstElement) * WFD_SINK_M_NUM);
2976 /* create pipeline */
2977 mainbin[WFD_SINK_M_PIPE].id = WFD_SINK_M_PIPE;
2978 mainbin[WFD_SINK_M_PIPE].gst = gst_pipeline_new("wfdsink");
2979 if (!mainbin[WFD_SINK_M_PIPE].gst) {
2980 wfd_sink_error("failed to create pipeline");
2985 MMWFDSINK_CREATE_ELEMENT(mainbin, WFD_SINK_M_SRC, wfd_sink->ini.name_of_source, "wfdsink_source", TRUE);
2986 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_SRC].gst, "src");
2987 if (mainbin[WFD_SINK_M_SRC].gst) {
2988 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_source(wfd_sink, mainbin[WFD_SINK_M_SRC].gst)) {
2989 wfd_sink_error("failed to prepare wfdsrc...");
2994 /* create rtpmp2tdepay */
2995 MMWFDSINK_CREATE_ELEMENT(mainbin, WFD_SINK_M_DEPAY, "rtpmp2tdepay", "wfdsink_depay", TRUE);
2996 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_DEPAY].gst, "src");
2997 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_DEPAY].gst, "sink");
2999 MMWFDSINK_TS_DATA_DUMP(wfd_sink, mainbin[WFD_SINK_M_DEPAY].gst, "src");
3001 /* create queue for ts */
3002 MMWFDSINK_CREATE_ELEMENT(mainbin, WFD_SINK_M_QUEUE, "queue", "ts_queue", TRUE);
3003 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_QUEUE].gst, "src");
3004 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_QUEUE].gst, "sink");
3005 g_object_set(G_OBJECT(mainbin[WFD_SINK_M_QUEUE].gst), "max-size-buffers", 200000, NULL);
3007 /* create valve for demux */
3008 MMWFDSINK_CREATE_ELEMENT(mainbin, WFD_SINK_M_D_VALVE, "valve", "demux_valve", TRUE);
3009 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_D_VALVE].gst, "src");
3010 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_D_VALVE].gst, "sink");
3012 /* create tsdemuxer*/
3013 MMWFDSINK_CREATE_ELEMENT(mainbin, WFD_SINK_M_DEMUX, wfd_sink->ini.name_of_tsdemux, "wfdsink_demux", TRUE);
3014 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_DEMUX].gst, "sink");
3015 if (mainbin[WFD_SINK_M_DEMUX].gst) {
3016 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_demux(wfd_sink, mainbin[WFD_SINK_M_DEMUX].gst)) {
3017 wfd_sink_error("failed to prepare demux...");
3022 /* create valve for audio */
3023 MMWFDSINK_CREATE_ELEMENT(mainbin, WFD_SINK_M_A_VALVE, "valve", "audio_valve", FALSE);
3024 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_A_VALVE].gst, "src");
3025 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_A_VALVE].gst, "sink");
3027 if (wfd_sink->ini.sink_session_mode & (WFD_SESSION_MODE_MIXED | WFD_SESSION_MODE_VIDEO_ONLY)) {
3028 /* create valve for video */
3029 MMWFDSINK_CREATE_ELEMENT(mainbin, WFD_SINK_M_V_VALVE, "valve", "video_valve", FALSE);
3030 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_V_VALVE].gst, "src");
3031 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_V_VALVE].gst, "sink");
3034 /* adding created elements to pipeline */
3035 if (!__mm_wfd_sink_gst_element_add_bucket_to_bin(GST_BIN_CAST(mainbin[WFD_SINK_M_PIPE].gst), element_bucket, FALSE)) {
3036 wfd_sink_error("failed to add elements");
3040 /* linking elements in the bucket by added order. */
3041 if (__mm_wfd_sink_gst_element_link_bucket(element_bucket) == -1) {
3042 wfd_sink_error("failed to link elements");
3046 /* connect bus callback */
3047 bus = gst_pipeline_get_bus(GST_PIPELINE(mainbin[WFD_SINK_M_PIPE].gst));
3049 wfd_sink_error("cannot get bus from pipeline.");
3053 /* add bus message callback*/
3054 wfd_sink->msg_callback_id = gst_bus_add_watch(bus, (GstBusFunc)_mm_wfd_sink_msg_callback, wfd_sink);
3056 /* set sync handler to get tag synchronously */
3057 gst_bus_set_sync_handler(bus, _mm_wfd_bus_sync_callback, wfd_sink, NULL);
3059 g_list_free(element_bucket);
3060 element_bucket = NULL;
3061 gst_object_unref(GST_OBJECT(bus));
3064 /* now we have completed mainbin. take it */
3065 wfd_sink->pipeline->mainbin = mainbin;
3067 wfd_sink_debug_fleave();
3069 return MM_ERROR_NONE;
3073 wfd_sink_error("ERROR : releasing pipeline");
3075 if (element_bucket) {
3076 g_list_free(element_bucket);
3077 element_bucket = NULL;
3080 /* release element which are not added to bin */
3081 for (i = 1; i < WFD_SINK_M_NUM; i++) { /* NOTE : skip pipeline */
3082 if (mainbin != NULL && mainbin[i].gst) {
3083 GstObject *parent = NULL;
3084 parent = gst_element_get_parent(mainbin[i].gst);
3087 gst_object_unref(GST_OBJECT(mainbin[i].gst));
3088 mainbin[i].gst = NULL;
3090 gst_object_unref(GST_OBJECT(parent));
3096 /* release mainbin with it's childs */
3097 if (mainbin != NULL && mainbin[WFD_SINK_M_PIPE].gst) {
3098 gst_object_unref(GST_OBJECT(mainbin[WFD_SINK_M_PIPE].gst));
3099 mainbin[WFD_SINK_M_PIPE].gst = NULL;
3102 MMWFDSINK_FREEIF(mainbin);
3104 MMWFDSINK_FREEIF(wfd_sink->pipeline);
3106 return MM_ERROR_WFD_INTERNAL;
3109 int __mm_wfd_sink_link_audio_decodebin(mm_wfd_sink_t *wfd_sink)
3111 MMWFDSinkGstElement *a_decodebin = NULL;
3112 MMWFDSinkGstElement *first_element = NULL;
3113 MMWFDSinkGstElement *last_element = NULL;
3114 GList *element_bucket = NULL;
3115 GstPad *sinkpad = NULL;
3116 GstPad *srcpad = NULL;
3117 GstPad *ghostpad = NULL;
3118 GList *list_temp = NULL;
3120 wfd_sink_debug_fenter();
3122 wfd_sink_return_val_if_fail(wfd_sink &&
3123 wfd_sink->pipeline &&
3124 wfd_sink->pipeline->a_decodebin &&
3125 wfd_sink->pipeline->a_decodebin[WFD_SINK_A_D_BIN].gst,
3126 MM_ERROR_WFD_NOT_INITIALIZED);
3128 if (wfd_sink->audio_decodebin_is_linked) {
3129 wfd_sink_debug("audio decodebin is already linked... nothing to do");
3130 return MM_ERROR_NONE;
3133 if (wfd_sink->stream_info.audio_stream_info.codec == MM_WFD_SINK_AUDIO_CODEC_NONE) {
3134 wfd_sink_debug("Skip link audio decodebin for none audio codec.");
3135 wfd_sink_debug_fleave();
3136 return MM_ERROR_NONE;
3139 /* take audio decodebin */
3140 a_decodebin = wfd_sink->pipeline->a_decodebin;
3142 /* check audio queue */
3143 if (a_decodebin[WFD_SINK_A_D_QUEUE].gst)
3144 element_bucket = g_list_append(element_bucket, &a_decodebin[WFD_SINK_A_D_QUEUE]);
3146 /* check audio hdcp */
3147 if (a_decodebin[WFD_SINK_A_D_HDCP].gst)
3148 element_bucket = g_list_append(element_bucket, &a_decodebin[WFD_SINK_A_D_HDCP]);
3150 /* check audio codec */
3151 switch (wfd_sink->stream_info.audio_stream_info.codec) {
3152 case MM_WFD_SINK_AUDIO_CODEC_LPCM:
3153 if (a_decodebin[WFD_SINK_A_D_LPCM_CONVERTER].gst)
3154 element_bucket = g_list_append(element_bucket, &a_decodebin[WFD_SINK_A_D_LPCM_CONVERTER]);
3155 if (a_decodebin[WFD_SINK_A_D_LPCM_FILTER].gst) {
3156 GstCaps *caps = NULL;
3157 element_bucket = g_list_append(element_bucket, &a_decodebin[WFD_SINK_A_D_LPCM_FILTER]);
3158 caps = gst_caps_new_simple("audio/x-raw",
3159 "rate", G_TYPE_INT, wfd_sink->stream_info.audio_stream_info.sample_rate,
3160 "channels", G_TYPE_INT, wfd_sink->stream_info.audio_stream_info.channels,
3161 "format", G_TYPE_STRING, "S16BE", NULL);
3163 g_object_set(G_OBJECT(a_decodebin[WFD_SINK_A_D_LPCM_CONVERTER].gst), "caps", caps, NULL);
3164 gst_object_unref(GST_OBJECT(caps));
3169 case MM_WFD_SINK_AUDIO_CODEC_AAC:
3170 if (a_decodebin[WFD_SINK_A_D_AAC_PARSE].gst)
3171 element_bucket = g_list_append(element_bucket, &a_decodebin[WFD_SINK_A_D_AAC_PARSE]);
3172 if (a_decodebin[WFD_SINK_A_D_AAC_DEC].gst)
3173 element_bucket = g_list_append(element_bucket, &a_decodebin[WFD_SINK_A_D_AAC_DEC]);
3176 case MM_WFD_SINK_AUDIO_CODEC_AC3:
3177 if (a_decodebin[WFD_SINK_A_D_AC3_PARSE].gst)
3178 element_bucket = g_list_append(element_bucket, &a_decodebin[WFD_SINK_A_D_AC3_PARSE]);
3179 if (a_decodebin[WFD_SINK_A_D_AC3_DEC].gst)
3180 element_bucket = g_list_append(element_bucket, &a_decodebin[WFD_SINK_A_D_AC3_DEC]);
3184 wfd_sink_error("audio codec is not decied yet. cannot link audio decodebin...");
3185 return MM_ERROR_WFD_INTERNAL;
3189 if (element_bucket == NULL) {
3190 wfd_sink_error("there are no elements to be linked in the audio decodebin, destroy it");
3191 if (MM_ERROR_NONE != __mm_wfd_sink_destroy_audio_decodebin(wfd_sink)) {
3192 wfd_sink_error("failed to destroy audio decodebin");
3198 /* adding elements to audio decodebin */
3199 if (!__mm_wfd_sink_gst_element_add_bucket_to_bin(GST_BIN_CAST(a_decodebin[WFD_SINK_A_D_BIN].gst), element_bucket, FALSE)) {
3200 wfd_sink_error("failed to add elements to audio decodebin");
3204 /* linking elements in the bucket by added order. */
3205 if (__mm_wfd_sink_gst_element_link_bucket(element_bucket) == -1) {
3206 wfd_sink_error("failed to link elements of the audio decodebin");
3210 /* get first element's sinkpad for creating ghostpad */
3211 list_temp = g_list_first(element_bucket);
3212 if (list_temp == NULL) {
3213 wfd_sink_error("failed to get first list of the element_bucket");
3217 first_element = (MMWFDSinkGstElement *)list_temp->data;
3218 if (!first_element) {
3219 wfd_sink_error("failed to get first element of the audio decodebin");
3223 sinkpad = gst_element_get_static_pad(GST_ELEMENT(first_element->gst), "sink");
3225 wfd_sink_error("failed to get sink pad from element(%s)",
3226 GST_STR_NULL(GST_ELEMENT_NAME(first_element->gst)));
3230 ghostpad = gst_ghost_pad_new("sink", sinkpad);
3232 wfd_sink_error("failed to create ghostpad of audio decodebin");
3236 if (FALSE == gst_element_add_pad(a_decodebin[WFD_SINK_A_D_BIN].gst, ghostpad)) {
3237 wfd_sink_error("failed to add ghostpad to audio decodebin");
3240 gst_object_unref(GST_OBJECT(sinkpad));
3244 /* get last element's src for creating ghostpad */
3245 list_temp = g_list_last(element_bucket);
3246 if (list_temp == NULL) {
3247 wfd_sink_error("failed to get last list of the element_bucket");
3251 last_element = (MMWFDSinkGstElement *)list_temp->data;
3252 if (!last_element) {
3253 wfd_sink_error("failed to get last element of the audio decodebin");
3257 srcpad = gst_element_get_static_pad(GST_ELEMENT(last_element->gst), "src");
3259 wfd_sink_error("failed to get src pad from element(%s)",
3260 GST_STR_NULL(GST_ELEMENT_NAME(last_element->gst)));
3264 ghostpad = gst_ghost_pad_new("src", srcpad);
3266 wfd_sink_error("failed to create ghostpad of audio decodebin");
3270 if (FALSE == gst_element_add_pad(a_decodebin[WFD_SINK_A_D_BIN].gst, ghostpad)) {
3271 wfd_sink_error("failed to add ghostpad to audio decodebin");
3274 gst_object_unref(GST_OBJECT(srcpad));
3277 g_list_free(element_bucket);
3280 wfd_sink->audio_decodebin_is_linked = TRUE;
3282 wfd_sink_debug_fleave();
3284 return MM_ERROR_NONE;
3289 gst_object_unref(GST_OBJECT(srcpad));
3294 gst_object_unref(GST_OBJECT(sinkpad));
3298 g_list_free(element_bucket);
3300 return MM_ERROR_WFD_INTERNAL;
3303 static int __mm_wfd_sink_prepare_audiosink(mm_wfd_sink_t *wfd_sink, GstElement *audio_sink)
3305 wfd_sink_debug_fenter();
3307 /* check audiosink is created */
3308 wfd_sink_return_val_if_fail(audio_sink, MM_ERROR_WFD_INVALID_ARGUMENT);
3309 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
3311 g_object_set(G_OBJECT(audio_sink), "provide-clock", FALSE, NULL);
3312 g_object_set(G_OBJECT(audio_sink), "buffer-time", 100000LL, NULL);
3313 g_object_set(G_OBJECT(audio_sink), "slave-method", 2, NULL);
3314 g_object_set(G_OBJECT(audio_sink), "async", wfd_sink->ini.audio_sink_async, NULL);
3315 g_object_set(G_OBJECT(audio_sink), "ts-offset", (gint64)wfd_sink->ini.sink_ts_offset, NULL);
3317 wfd_sink_debug_fleave();
3319 return MM_ERROR_NONE;
3322 static int __mm_wfd_sink_destroy_audio_decodebin(mm_wfd_sink_t *wfd_sink)
3324 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
3325 MMWFDSinkGstElement *a_decodebin = NULL;
3326 GstObject *parent = NULL;
3329 wfd_sink_debug_fenter();
3331 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
3333 if (wfd_sink->pipeline &&
3334 wfd_sink->pipeline->a_decodebin &&
3335 wfd_sink->pipeline->a_decodebin[WFD_SINK_A_D_BIN].gst) {
3336 a_decodebin = wfd_sink->pipeline->a_decodebin;
3338 wfd_sink_debug("audio decodebin is not created, nothing to destroy");
3339 return MM_ERROR_NONE;
3342 parent = gst_element_get_parent(a_decodebin[WFD_SINK_A_D_BIN].gst);
3344 wfd_sink_debug("audio decodebin has no parent.. need to relase by itself");
3346 if (GST_STATE(a_decodebin[WFD_SINK_A_D_BIN].gst) >= GST_STATE_READY) {
3347 wfd_sink_debug("try to change state of audio decodebin to NULL");
3348 ret = gst_element_set_state(a_decodebin[WFD_SINK_A_D_BIN].gst, GST_STATE_NULL);
3349 if (ret != GST_STATE_CHANGE_SUCCESS) {
3350 wfd_sink_error("failed to change state of audio decodebin to NULL");
3351 return MM_ERROR_WFD_INTERNAL;
3355 /* release element which are not added to bin */
3356 for (i = 1; i < WFD_SINK_A_D_NUM; i++) { /* NOTE : skip bin */
3357 if (a_decodebin[i].gst) {
3358 parent = gst_element_get_parent(a_decodebin[i].gst);
3360 wfd_sink_debug("unref %s(current ref %d)",
3361 GST_STR_NULL(GST_ELEMENT_NAME(a_decodebin[i].gst)),
3362 ((GObject *) a_decodebin[i].gst)->ref_count);
3363 gst_object_unref(GST_OBJECT(a_decodebin[i].gst));
3364 a_decodebin[i].gst = NULL;
3366 wfd_sink_debug("%s has parent.(current ref %d)",
3367 GST_STR_NULL(GST_ELEMENT_NAME(a_decodebin[i].gst)),
3368 ((GObject *) a_decodebin[i].gst)->ref_count);
3369 gst_object_unref(GST_OBJECT(parent));
3375 /* release audio decodebin with it's childs */
3376 if (a_decodebin[WFD_SINK_A_D_BIN].gst) {
3377 gst_object_unref(GST_OBJECT(a_decodebin[WFD_SINK_A_D_BIN].gst));
3378 a_decodebin[WFD_SINK_A_D_BIN].gst = NULL;
3382 wfd_sink_debug("audio decodebin has parent(%s), unref it ",
3383 GST_STR_NULL(GST_OBJECT_NAME(GST_OBJECT(parent))));
3385 gst_object_unref(GST_OBJECT(parent));
3389 wfd_sink->audio_decodebin_is_linked = FALSE;
3391 MMWFDSINK_FREEIF(wfd_sink->pipeline->a_decodebin);
3393 wfd_sink_debug_fleave();
3395 return MM_ERROR_NONE;
3398 int __mm_wfd_sink_create_audio_decodebin(mm_wfd_sink_t *wfd_sink)
3400 MMWFDSinkGstElement *a_decodebin = NULL;
3401 gint audio_codec = WFD_AUDIO_UNKNOWN;
3402 GList *element_bucket = NULL;
3403 gboolean link = TRUE;
3406 wfd_sink_debug_fenter();
3408 wfd_sink_return_val_if_fail(wfd_sink &&
3410 MM_ERROR_WFD_NOT_INITIALIZED);
3412 if (wfd_sink->pipeline->a_decodebin != NULL) {
3413 wfd_sink_error("The audio decode bin is already created.");
3414 return MM_ERROR_NONE;
3417 if (wfd_sink->stream_info.audio_stream_info.codec == MM_WFD_SINK_AUDIO_CODEC_NONE) {
3418 wfd_sink_debug("Skip create audio decodebin for none audio codec.");
3419 wfd_sink_debug_fleave();
3420 return MM_ERROR_NONE;
3423 /* check audio decodebin could be linked now */
3424 switch (wfd_sink->stream_info.audio_stream_info.codec) {
3425 case MM_WFD_SINK_AUDIO_CODEC_AAC:
3426 audio_codec = WFD_AUDIO_AAC;
3429 case MM_WFD_SINK_AUDIO_CODEC_AC3:
3430 audio_codec = WFD_AUDIO_AC3;
3433 case MM_WFD_SINK_AUDIO_CODEC_LPCM:
3434 audio_codec = WFD_AUDIO_LPCM;
3437 case MM_WFD_SINK_AUDIO_CODEC_NONE:
3439 wfd_sink_debug("audio decodebin could NOT be linked now, just create");
3440 audio_codec = wfd_sink->ini.wfd_audio_codecs.audio_codec;
3446 a_decodebin = (MMWFDSinkGstElement *)g_malloc0(sizeof(MMWFDSinkGstElement) * WFD_SINK_A_D_NUM);
3448 wfd_sink_error("failed to allocate memory for audio decodebin");
3449 return MM_ERROR_WFD_NO_FREE_SPACE;
3452 memset(a_decodebin, 0, sizeof(MMWFDSinkGstElement) * WFD_SINK_A_D_NUM);
3454 /* create audio decodebin */
3455 a_decodebin[WFD_SINK_A_D_BIN].id = WFD_SINK_A_D_BIN;
3456 a_decodebin[WFD_SINK_A_D_BIN].gst = gst_bin_new("audio_deocebin");
3457 if (!a_decodebin[WFD_SINK_A_D_BIN].gst) {
3458 wfd_sink_error("failed to create audio decodebin");
3463 MMWFDSINK_CREATE_ELEMENT(a_decodebin, WFD_SINK_A_D_QUEUE, "queue", "audio_queue", FALSE);
3464 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_QUEUE].gst, "sink");
3465 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_QUEUE].gst, "src");
3466 if (a_decodebin[WFD_SINK_A_D_QUEUE].gst)
3467 __mm_wfd_sink_prepare_queue(wfd_sink, a_decodebin[WFD_SINK_A_D_QUEUE].gst);
3470 MMWFDSINK_CREATE_ELEMENT(a_decodebin, WFD_SINK_A_D_HDCP, wfd_sink->ini.name_of_audio_hdcp, "audio_hdcp", FALSE);
3471 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_HDCP].gst, "sink");
3472 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_HDCP].gst, "src");
3475 audio_codec = wfd_sink->ini.wfd_audio_codecs.audio_codec;
3476 if (audio_codec & WFD_AUDIO_LPCM) {
3477 /* create LPCM converter */
3478 MMWFDSINK_CREATE_ELEMENT(a_decodebin, WFD_SINK_A_D_LPCM_CONVERTER, wfd_sink->ini.name_of_lpcm_converter, "audio_lpcm_convert", FALSE);
3479 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_LPCM_CONVERTER].gst, "sink");
3480 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_LPCM_CONVERTER].gst, "src");
3482 /* create LPCM filter */
3483 MMWFDSINK_CREATE_ELEMENT(a_decodebin, WFD_SINK_A_D_LPCM_FILTER, wfd_sink->ini.name_of_lpcm_filter, "audio_lpcm_filter", FALSE);
3484 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_LPCM_FILTER].gst, "sink");
3485 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_LPCM_FILTER].gst, "src");
3488 if (audio_codec & WFD_AUDIO_AAC) {
3489 /* create AAC parse */
3490 MMWFDSINK_CREATE_ELEMENT(a_decodebin, WFD_SINK_A_D_AAC_PARSE, wfd_sink->ini.name_of_aac_parser, "audio_aac_parser", FALSE);
3491 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_AAC_PARSE].gst, "sink");
3492 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_AAC_PARSE].gst, "src");
3494 /* create AAC decoder */
3495 MMWFDSINK_CREATE_ELEMENT(a_decodebin, WFD_SINK_A_D_AAC_DEC, wfd_sink->ini.name_of_aac_decoder, "audio_aac_dec", FALSE);
3496 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_AAC_DEC].gst, "sink");
3497 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_AAC_DEC].gst, "src");
3500 if (audio_codec & WFD_AUDIO_AC3) {
3501 /* create AC3 parser */
3502 MMWFDSINK_CREATE_ELEMENT(a_decodebin, WFD_SINK_A_D_AC3_PARSE, wfd_sink->ini.name_of_ac3_parser, "audio_ac3_parser", FALSE);
3503 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_AC3_PARSE].gst, "sink");
3504 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_AC3_PARSE].gst, "src");
3506 /* create AC3 decoder */
3507 MMWFDSINK_CREATE_ELEMENT(a_decodebin, WFD_SINK_A_D_AC3_DEC, wfd_sink->ini.name_of_ac3_decoder, "audio_ac3_dec", FALSE);
3508 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_AC3_DEC].gst, "sink");
3509 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_AC3_DEC].gst, "src");
3512 g_list_free(element_bucket);
3515 wfd_sink->pipeline->a_decodebin = a_decodebin;
3517 /* link audio decodebin if audio codec is fixed */
3519 if (MM_ERROR_NONE != __mm_wfd_sink_link_audio_decodebin(wfd_sink)) {
3520 wfd_sink_error("failed to link audio decodebin, destroy audio decodebin");
3521 __mm_wfd_sink_destroy_audio_decodebin(wfd_sink);
3522 return MM_ERROR_WFD_INTERNAL;
3526 wfd_sink_debug_fleave();
3528 return MM_ERROR_NONE;
3531 wfd_sink_error("failed to create audio decodebin, release all");
3533 g_list_free(element_bucket);
3535 /* release element which are not added to bin */
3536 for (i = 1; i < WFD_SINK_A_D_NUM; i++) { /* NOTE : skip bin */
3537 if (a_decodebin != NULL && a_decodebin[i].gst) {
3538 GstObject *parent = NULL;
3539 parent = gst_element_get_parent(a_decodebin[i].gst);
3542 gst_object_unref(GST_OBJECT(a_decodebin[i].gst));
3543 a_decodebin[i].gst = NULL;
3545 gst_object_unref(GST_OBJECT(parent));
3551 /* release audio decodebin with it's childs */
3552 if (a_decodebin != NULL && a_decodebin[WFD_SINK_A_D_BIN].gst) {
3553 gst_object_unref(GST_OBJECT(a_decodebin[WFD_SINK_A_D_BIN].gst));
3554 a_decodebin[WFD_SINK_A_D_BIN].gst = NULL;
3557 MMWFDSINK_FREEIF(a_decodebin);
3559 return MM_ERROR_WFD_INTERNAL;
3562 static int __mm_wfd_sink_destroy_audio_sinkbin(mm_wfd_sink_t *wfd_sink)
3564 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
3565 MMWFDSinkGstElement *a_sinkbin = NULL;
3566 GstObject *parent = NULL;
3569 wfd_sink_debug_fenter();
3571 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
3573 if (wfd_sink->pipeline &&
3574 wfd_sink->pipeline->a_sinkbin &&
3575 wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_BIN].gst) {
3576 a_sinkbin = wfd_sink->pipeline->a_sinkbin;
3578 wfd_sink_debug("audio sinkbin is not created, nothing to destroy");
3579 return MM_ERROR_NONE;
3582 parent = gst_element_get_parent(a_sinkbin[WFD_SINK_A_S_BIN].gst);
3584 wfd_sink_debug("audio sinkbin has no parent.. need to relase by itself");
3586 if (GST_STATE(a_sinkbin[WFD_SINK_A_S_BIN].gst) >= GST_STATE_READY) {
3587 wfd_sink_debug("try to change state of audio sinkbin to NULL");
3588 ret = gst_element_set_state(a_sinkbin[WFD_SINK_A_S_BIN].gst, GST_STATE_NULL);
3589 if (ret != GST_STATE_CHANGE_SUCCESS) {
3590 wfd_sink_error("failed to change state of audio sinkbin to NULL");
3591 return MM_ERROR_WFD_INTERNAL;
3595 /* release element which are not added to bin */
3596 for (i = 1; i < WFD_SINK_A_S_NUM; i++) { /* NOTE : skip bin */
3597 if (a_sinkbin[i].gst) {
3598 parent = gst_element_get_parent(a_sinkbin[i].gst);
3600 wfd_sink_debug("unref %s(current ref %d)",
3601 GST_STR_NULL(GST_ELEMENT_NAME(a_sinkbin[i].gst)),
3602 ((GObject *) a_sinkbin[i].gst)->ref_count);
3603 gst_object_unref(GST_OBJECT(a_sinkbin[i].gst));
3604 a_sinkbin[i].gst = NULL;
3606 wfd_sink_debug("%s has parent.(current ref %d)",
3607 GST_STR_NULL(GST_ELEMENT_NAME(a_sinkbin[i].gst)),
3608 ((GObject *) a_sinkbin[i].gst)->ref_count);
3609 gst_object_unref(GST_OBJECT(parent));
3615 /* release audio sinkbin with it's childs */
3616 if (a_sinkbin[WFD_SINK_A_S_BIN].gst) {
3617 gst_object_unref(GST_OBJECT(a_sinkbin[WFD_SINK_A_S_BIN].gst));
3618 a_sinkbin[WFD_SINK_A_S_BIN].gst = NULL;
3621 wfd_sink_debug("audio sinkbin has parent(%s), unref it ",
3622 GST_STR_NULL(GST_OBJECT_NAME(GST_OBJECT(parent))));
3624 gst_object_unref(GST_OBJECT(parent));
3628 MMWFDSINK_FREEIF(wfd_sink->pipeline->a_sinkbin);
3630 wfd_sink_debug_fleave();
3632 return MM_ERROR_NONE;
3635 int __mm_wfd_sink_create_audio_sinkbin(mm_wfd_sink_t *wfd_sink)
3637 MMWFDSinkGstElement *a_sinkbin = NULL;
3638 MMWFDSinkGstElement *first_element = NULL;
3639 GList *element_bucket = NULL;
3640 GstPad *ghostpad = NULL;
3643 GList *list_temp = NULL;
3645 wfd_sink_debug_fenter();
3647 wfd_sink_return_val_if_fail(wfd_sink &&
3649 MM_ERROR_WFD_NOT_INITIALIZED);
3651 if (wfd_sink->pipeline->a_sinkbin != NULL) {
3652 wfd_sink_error("The audio sink bin is already created.");
3653 return MM_ERROR_NONE;
3656 if (wfd_sink->stream_info.audio_stream_info.codec == MM_WFD_SINK_AUDIO_CODEC_NONE) {
3657 wfd_sink_error("Skip create audio sink bin for non audio codec. %d", wfd_sink->stream_info.audio_stream_info.codec);
3658 wfd_sink_debug_fleave();
3659 return MM_ERROR_NONE;
3663 a_sinkbin = (MMWFDSinkGstElement *)g_malloc0(sizeof(MMWFDSinkGstElement) * WFD_SINK_A_S_NUM);
3665 wfd_sink_error("failed to allocate memory for audio sinkbin");
3666 return MM_ERROR_WFD_NO_FREE_SPACE;
3669 memset(a_sinkbin, 0, sizeof(MMWFDSinkGstElement) * WFD_SINK_A_S_NUM);
3671 /* create audio sinkbin */
3672 a_sinkbin[WFD_SINK_A_S_BIN].id = WFD_SINK_A_S_BIN;
3673 a_sinkbin[WFD_SINK_A_S_BIN].gst = gst_bin_new("audio_sinkbin");
3674 if (!a_sinkbin[WFD_SINK_A_S_BIN].gst) {
3675 wfd_sink_error("failed to create audio sinkbin");
3679 /* create resampler */
3680 MMWFDSINK_CREATE_ELEMENT(a_sinkbin, WFD_SINK_A_S_RESAMPLER,
3681 wfd_sink->ini.name_of_audio_resampler, "audio_resampler", TRUE);
3682 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_sinkbin[WFD_SINK_A_S_RESAMPLER].gst, "sink");
3683 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_sinkbin[WFD_SINK_A_S_RESAMPLER].gst, "src");
3686 MMWFDSINK_CREATE_ELEMENT(a_sinkbin, WFD_SINK_A_S_VOLUME,
3687 wfd_sink->ini.name_of_audio_volume, "audio_volume", TRUE);
3688 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_sinkbin[WFD_SINK_A_S_VOLUME].gst, "sink");
3689 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_sinkbin[WFD_SINK_A_S_VOLUME].gst, "src");
3691 MMWFDSINK_CREATE_ELEMENT(a_sinkbin, WFD_SINK_A_S_QUEUE,
3692 wfd_sink->ini.name_of_audio_sinkbin_queue, "audio_sinkbin_queue", TRUE);
3693 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_sinkbin[WFD_SINK_A_S_QUEUE].gst, "sink");
3694 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_sinkbin[WFD_SINK_A_S_QUEUE].gst, "src");
3697 MMWFDSINK_CREATE_ELEMENT(a_sinkbin, WFD_SINK_A_S_SINK,
3698 wfd_sink->ini.name_of_audio_sink, "audio_sink", TRUE);
3699 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_sinkbin[WFD_SINK_A_S_SINK].gst, "sink");
3700 if (a_sinkbin[WFD_SINK_A_S_SINK].gst) {
3701 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_audiosink(wfd_sink, a_sinkbin[WFD_SINK_A_S_SINK].gst)) {
3702 wfd_sink_error("failed to set audio sink property....");
3707 /* adding created elements to audio sinkbin */
3708 if (!__mm_wfd_sink_gst_element_add_bucket_to_bin(GST_BIN_CAST(a_sinkbin[WFD_SINK_A_S_BIN].gst), element_bucket, FALSE)) {
3709 wfd_sink_error("failed to add elements to audio sinkbin");
3713 /* linking elements in the bucket by added order. */
3714 if (__mm_wfd_sink_gst_element_link_bucket(element_bucket) == -1) {
3715 wfd_sink_error("failed to link elements fo the audio sinkbin");
3719 /* get first element's of the audio sinkbin */
3720 list_temp = g_list_first(element_bucket);
3721 if (list_temp == NULL) {
3722 wfd_sink_error("failed to get first list of the element_bucket");
3726 first_element = (MMWFDSinkGstElement *)list_temp->data;
3727 if (!first_element) {
3728 wfd_sink_error("failed to get first element of the audio sinkbin");
3732 /* get first element's sinkpad for creating ghostpad */
3733 pad = gst_element_get_static_pad(GST_ELEMENT(first_element->gst), "sink");
3735 wfd_sink_error("failed to get sink pad from element(%s)",
3736 GST_STR_NULL(GST_ELEMENT_NAME(first_element->gst)));
3740 ghostpad = gst_ghost_pad_new("sink", pad);
3742 wfd_sink_error("failed to create ghostpad of audio sinkbin");
3746 if (FALSE == gst_element_add_pad(a_sinkbin[WFD_SINK_A_S_BIN].gst, ghostpad)) {
3747 wfd_sink_error("failed to add ghostpad to audio sinkbin");
3750 gst_object_unref(GST_OBJECT(pad));
3753 g_list_free(element_bucket);
3754 element_bucket = NULL;
3757 wfd_sink->pipeline->a_sinkbin = a_sinkbin;
3759 wfd_sink_debug_fleave();
3761 return MM_ERROR_NONE;
3764 wfd_sink_error("failed to create audio sinkbin, releasing all");
3767 gst_object_unref(GST_OBJECT(pad));
3771 gst_object_unref(GST_OBJECT(ghostpad));
3774 if (element_bucket) {
3775 g_list_free(element_bucket);
3776 element_bucket = NULL;
3779 /* release element which are not added to bin */
3780 for (i = 1; i < WFD_SINK_A_S_NUM; i++) { /* NOTE : skip bin */
3781 if (a_sinkbin != NULL && a_sinkbin[i].gst) {
3782 GstObject *parent = NULL;
3783 parent = gst_element_get_parent(a_sinkbin[i].gst);
3786 gst_object_unref(GST_OBJECT(a_sinkbin[i].gst));
3787 a_sinkbin[i].gst = NULL;
3789 gst_object_unref(GST_OBJECT(parent));
3795 /* release audio sinkbin with it's childs */
3796 if (a_sinkbin != NULL && a_sinkbin[WFD_SINK_A_S_BIN].gst) {
3797 gst_object_unref(GST_OBJECT(a_sinkbin[WFD_SINK_A_S_BIN].gst));
3798 a_sinkbin[WFD_SINK_A_S_BIN].gst = NULL;
3800 MMWFDSINK_FREEIF(a_sinkbin);
3802 return MM_ERROR_WFD_INTERNAL;
3805 int __mm_wfd_sink_link_video_decodebin(mm_wfd_sink_t *wfd_sink)
3807 MMWFDSinkGstElement *v_decodebin = NULL;
3808 MMWFDSinkGstElement *first_element = NULL;
3809 MMWFDSinkGstElement *last_element = NULL;
3810 GList *element_bucket = NULL;
3811 GList *list_temp = NULL;
3812 GstPad *sinkpad = NULL;
3813 GstPad *srcpad = NULL;
3814 GstPad *ghostpad = NULL;
3816 wfd_sink_debug_fenter();
3818 wfd_sink_return_val_if_fail(wfd_sink &&
3819 wfd_sink->pipeline &&
3820 wfd_sink->pipeline->v_decodebin &&
3821 wfd_sink->pipeline->v_decodebin[WFD_SINK_V_D_BIN].gst,
3822 MM_ERROR_WFD_NOT_INITIALIZED);
3824 if (wfd_sink->video_decodebin_is_linked) {
3825 wfd_sink_debug("video decodebin is already linked... nothing to do");
3826 return MM_ERROR_NONE;
3829 /* take video decodebin */
3830 v_decodebin = wfd_sink->pipeline->v_decodebin;
3832 /* check video queue */
3833 if (v_decodebin[WFD_SINK_V_D_QUEUE].gst)
3834 element_bucket = g_list_append(element_bucket, &v_decodebin[WFD_SINK_V_D_QUEUE]);
3836 /* check video hdcp */
3837 if (v_decodebin[WFD_SINK_V_D_HDCP].gst)
3838 element_bucket = g_list_append(element_bucket, &v_decodebin[WFD_SINK_V_D_HDCP]);
3840 /* check video codec */
3841 switch (wfd_sink->stream_info.video_stream_info.codec) {
3842 case MM_WFD_SINK_VIDEO_CODEC_H264:
3843 if (v_decodebin[WFD_SINK_V_D_H264_PARSE].gst)
3844 element_bucket = g_list_append(element_bucket, &v_decodebin[WFD_SINK_V_D_H264_PARSE]);
3845 if (v_decodebin[WFD_SINK_V_D_H264_DEC].gst)
3846 element_bucket = g_list_append(element_bucket, &v_decodebin[WFD_SINK_V_D_H264_DEC]);
3849 case MM_WFD_SINK_VIDEO_CODEC_H265:
3850 if (v_decodebin[WFD_SINK_V_D_H265_PARSE].gst)
3851 element_bucket = g_list_append(element_bucket, &v_decodebin[WFD_SINK_V_D_H265_PARSE]);
3852 if (v_decodebin[WFD_SINK_V_D_H265_DEC].gst)
3853 element_bucket = g_list_append(element_bucket, &v_decodebin[WFD_SINK_V_D_H265_DEC]);
3857 wfd_sink_error("video codec is not decied yet. cannot link video decpdebin...");
3858 return MM_ERROR_WFD_INTERNAL;
3862 if (element_bucket == NULL) {
3863 wfd_sink_error("there are no elements to be linked in the video decodebin, destroy it");
3864 if (MM_ERROR_NONE != __mm_wfd_sink_destroy_video_decodebin(wfd_sink)) {
3865 wfd_sink_error("failed to destroy video decodebin");
3871 /* adding elements to video decodebin */
3872 if (!__mm_wfd_sink_gst_element_add_bucket_to_bin(GST_BIN_CAST(v_decodebin[WFD_SINK_V_D_BIN].gst), element_bucket, FALSE)) {
3873 wfd_sink_error("failed to add elements to video decodebin");
3877 /* linking elements in the bucket by added order. */
3878 if (__mm_wfd_sink_gst_element_link_bucket(element_bucket) == -1) {
3879 wfd_sink_error("failed to link elements of the video decodebin");
3883 /* get first element's sinkpad for creating ghostpad */
3884 list_temp = g_list_first(element_bucket);
3885 if (list_temp == NULL) {
3886 wfd_sink_error("failed to get first list of the element_bucket");
3890 first_element = (MMWFDSinkGstElement *)list_temp->data;
3891 if (!first_element) {
3892 wfd_sink_error("failed to get first element of the video decodebin");
3896 sinkpad = gst_element_get_static_pad(GST_ELEMENT(first_element->gst), "sink");
3898 wfd_sink_error("failed to get sink pad from element(%s)",
3899 GST_STR_NULL(GST_ELEMENT_NAME(first_element->gst)));
3903 ghostpad = gst_ghost_pad_new("sink", sinkpad);
3905 wfd_sink_error("failed to create ghostpad of video decodebin");
3909 if (FALSE == gst_element_add_pad(v_decodebin[WFD_SINK_V_D_BIN].gst, ghostpad)) {
3910 wfd_sink_error("failed to add ghostpad to video decodebin");
3913 gst_object_unref(GST_OBJECT(sinkpad));
3917 /* get last element's src for creating ghostpad */
3918 list_temp = g_list_last(element_bucket);
3919 if (list_temp == NULL) {
3920 wfd_sink_error("failed to get last list of the element_bucket");
3924 last_element = (MMWFDSinkGstElement *)list_temp->data;
3925 if (!last_element) {
3926 wfd_sink_error("failed to get last element of the video decodebin");
3930 srcpad = gst_element_get_static_pad(GST_ELEMENT(last_element->gst), "src");
3932 wfd_sink_error("failed to get src pad from element(%s)",
3933 GST_STR_NULL(GST_ELEMENT_NAME(last_element->gst)));
3937 ghostpad = gst_ghost_pad_new("src", srcpad);
3939 wfd_sink_error("failed to create ghostpad of video decodebin");
3943 if (FALSE == gst_element_add_pad(v_decodebin[WFD_SINK_V_D_BIN].gst, ghostpad)) {
3944 wfd_sink_error("failed to add ghostpad to video decodebin");
3947 gst_object_unref(GST_OBJECT(srcpad));
3950 g_list_free(element_bucket);
3953 wfd_sink->video_decodebin_is_linked = TRUE;
3955 wfd_sink_debug_fleave();
3957 return MM_ERROR_NONE;
3961 if (srcpad != NULL) {
3962 gst_object_unref(GST_OBJECT(srcpad));
3966 if (sinkpad != NULL) {
3967 gst_object_unref(GST_OBJECT(sinkpad));
3971 if (element_bucket != NULL) {
3972 g_list_free(element_bucket);
3973 element_bucket = NULL;
3975 return MM_ERROR_WFD_INTERNAL;
3978 static int __mm_wfd_sink_prepare_videodec(mm_wfd_sink_t *wfd_sink, GstElement *video_dec)
3980 wfd_sink_debug_fenter();
3982 /* check video decoder is created */
3983 wfd_sink_return_val_if_fail(video_dec, MM_ERROR_WFD_INVALID_ARGUMENT);
3984 wfd_sink_return_val_if_fail(wfd_sink && wfd_sink->attrs, MM_ERROR_WFD_NOT_INITIALIZED);
3986 wfd_sink_debug_fleave();
3988 return MM_ERROR_NONE;
3991 static int __mm_wfd_sink_prepare_videosink(mm_wfd_sink_t *wfd_sink, GstElement *video_sink)
3993 gboolean visible = TRUE;
3994 gint surface_type = MM_DISPLAY_SURFACE_OVERLAY;
3996 wfd_sink_debug_fenter();
3998 /* check videosink is created */
3999 wfd_sink_return_val_if_fail(video_sink, MM_ERROR_WFD_INVALID_ARGUMENT);
4000 wfd_sink_return_val_if_fail(wfd_sink && wfd_sink->attrs, MM_ERROR_WFD_NOT_INITIALIZED);
4002 /* update display surface */
4003 mm_attrs_get_int_by_name(wfd_sink->attrs, "display_surface_type", &surface_type);
4004 wfd_sink_info("check display surface type attribute: %d", surface_type);
4005 mm_attrs_get_int_by_name(wfd_sink->attrs, "display_visible", &visible);
4006 wfd_sink_info("check display visible attribute: %d", visible);
4008 if (FALSE == visible) {
4009 wfd_sink_info("skipped to prepare video sink. display_visible is FALSE.");
4010 g_object_set(G_OBJECT(video_sink), "visible", visible, NULL);
4011 return MM_ERROR_NONE;
4014 /* configuring display */
4015 switch (surface_type) {
4016 case MM_DISPLAY_SURFACE_EVAS: {
4017 void *object = NULL;
4020 /* common case if using evas surface */
4021 mm_attrs_get_data_by_name(wfd_sink->attrs, "display_overlay", &object);
4022 mm_attrs_get_int_by_name(wfd_sink->attrs, "display_evas_do_scaling", &scaling);
4024 wfd_sink_debug("set video param : evas-object %x", object);
4025 g_object_set(G_OBJECT(video_sink), "evas-object", object, NULL);
4027 wfd_sink_error("no evas object");
4028 return MM_ERROR_WFD_INTERNAL;
4033 case MM_DISPLAY_SURFACE_OVERLAY: {
4034 static unsigned int wl_surface_id = 0;
4035 static void *display_overlay = NULL;
4036 int wl_window_x = 0;
4037 int wl_window_y = 0;
4038 int wl_window_width = 0;
4039 int wl_window_height = 0;
4040 struct wl_surface *wl_surface = NULL;
4041 struct wl_display *wl_display = NULL;
4042 Ecore_Wl_Window *wl_window = NULL;
4043 wl_client *wlclient = NULL;
4044 Evas_Object *obj = NULL;
4045 void *object = NULL;
4046 const char *object_type = NULL;
4049 mm_attrs_get_data_by_name(wfd_sink->attrs, "display_overlay", &object);
4051 if (object != NULL) {
4052 obj = (Evas_Object *)object;
4053 object_type = evas_object_type_get(obj);
4054 wfd_sink_debug("window object type : %s", object_type);
4056 /* wayland overlay surface */
4057 LOGI("Wayland overlay surface type");
4058 evas_object_geometry_get(obj, &wl_window_x, &wl_window_y, &wl_window_width, &wl_window_height);
4060 wfd_sink_debug("x[%d] y[%d] width[%d] height[%d]", wl_window_x, wl_window_y,
4061 wl_window_width, wl_window_height);
4063 wl_window = elm_win_wl_window_get(obj);
4064 wl_surface = (struct wl_surface *) ecore_wl_window_surface_get(wl_window);
4066 /* get wl_display */
4067 wl_display = (struct wl_display *) ecore_wl_display_get();
4069 wfd_sink_debug("previous display object : %p current object : %p", display_overlay, object);
4070 if (wl_surface && wl_display && (wl_surface_id == 0 || display_overlay != object)) {
4071 wfd_sink_debug("surface = %p, wl_display = %p", wl_surface, wl_display);
4072 display_overlay = object;
4074 ret = mm_wfd_sink_wlclient_create(&wlclient);
4075 if (ret != MM_ERROR_NONE) {
4076 wfd_sink_error("Wayland client create failure");
4079 wfd_sink_debug("Try to get surface id");
4081 wl_surface_id = mm_wfd_sink_wlclient_get_wl_window_wl_surface_id(wlclient, wl_surface, wl_display);
4083 wfd_sink_debug("wl_surface_id = %d", wl_surface_id);
4090 wfd_sink_debug("set video param : surface_id %d", wl_surface_id);
4092 if (USE_EXTERNAL_WL_DISPLAY_HANDLE) {
4093 gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(video_sink),
4096 gst_video_overlay_set_wl_window_wl_surface_id(GST_VIDEO_OVERLAY(video_sink),
4100 /* After setting window handle, set render rectangle */
4101 gst_video_overlay_set_render_rectangle(GST_VIDEO_OVERLAY(video_sink),
4102 wl_window_x, wl_window_y, wl_window_width, wl_window_height);
4104 wfd_sink_debug("display object is NULL!");
4105 return MM_ERROR_WFD_INTERNAL;
4110 case MM_DISPLAY_SURFACE_NULL: {
4112 wfd_sink_error("Not Supported Surface.");
4113 return MM_ERROR_WFD_INTERNAL;
4117 wfd_sink_error("Not Supported Surface.(default case)");
4118 return MM_ERROR_WFD_INTERNAL;
4123 g_object_set(G_OBJECT(video_sink), "qos", FALSE, NULL);
4124 g_object_set(G_OBJECT(video_sink), "async", wfd_sink->ini.video_sink_async, NULL);
4125 g_object_set(G_OBJECT(video_sink), "max-lateness", (gint64)wfd_sink->ini.video_sink_max_lateness, NULL);
4126 g_object_set(G_OBJECT(video_sink), "visible", visible, NULL);
4127 g_object_set(G_OBJECT(video_sink), "ts-offset", (gint64)(wfd_sink->ini.sink_ts_offset), NULL);
4129 wfd_sink_debug_fleave();
4131 return MM_ERROR_NONE;
4134 static int __mm_wfd_sink_destroy_video_decodebin(mm_wfd_sink_t *wfd_sink)
4136 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
4137 MMWFDSinkGstElement *v_decodebin = NULL;
4138 GstObject *parent = NULL;
4141 wfd_sink_debug_fenter();
4143 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
4145 if (wfd_sink->pipeline &&
4146 wfd_sink->pipeline->v_decodebin &&
4147 wfd_sink->pipeline->v_decodebin[WFD_SINK_V_D_BIN].gst) {
4148 v_decodebin = wfd_sink->pipeline->v_decodebin;
4150 wfd_sink_debug("video decodebin is not created, nothing to destroy");
4151 return MM_ERROR_NONE;
4155 parent = gst_element_get_parent(v_decodebin[WFD_SINK_V_D_BIN].gst);
4157 wfd_sink_debug("video decodebin has no parent.. need to relase by itself");
4159 if (GST_STATE(v_decodebin[WFD_SINK_V_D_BIN].gst) >= GST_STATE_READY) {
4160 wfd_sink_debug("try to change state of video decodebin to NULL");
4161 ret = gst_element_set_state(v_decodebin[WFD_SINK_V_D_BIN].gst, GST_STATE_NULL);
4162 if (ret != GST_STATE_CHANGE_SUCCESS) {
4163 wfd_sink_error("failed to change state of video decodebin to NULL");
4164 return MM_ERROR_WFD_INTERNAL;
4167 /* release element which are not added to bin */
4168 for (i = 1; i < WFD_SINK_V_D_NUM; i++) { /* NOTE : skip bin */
4169 if (v_decodebin[i].gst) {
4170 parent = gst_element_get_parent(v_decodebin[i].gst);
4172 wfd_sink_debug("unref %s(current ref %d)",
4173 GST_STR_NULL(GST_ELEMENT_NAME(v_decodebin[i].gst)),
4174 ((GObject *) v_decodebin[i].gst)->ref_count);
4175 gst_object_unref(GST_OBJECT(v_decodebin[i].gst));
4176 v_decodebin[i].gst = NULL;
4178 wfd_sink_debug("%s has parent.(current ref %d)",
4179 GST_STR_NULL(GST_ELEMENT_NAME(v_decodebin[i].gst)),
4180 ((GObject *) v_decodebin[i].gst)->ref_count);
4181 gst_object_unref(GST_OBJECT(parent));
4186 /* release video decodebin with it's childs */
4187 if (v_decodebin[WFD_SINK_V_D_BIN].gst) {
4188 wfd_sink_debug("unref %s(current ref %d)",
4189 GST_STR_NULL(GST_ELEMENT_NAME(v_decodebin[WFD_SINK_V_D_BIN].gst)),
4190 ((GObject *)v_decodebin[WFD_SINK_V_D_BIN].gst)->ref_count);
4192 gst_object_unref(GST_OBJECT(v_decodebin[WFD_SINK_V_D_BIN].gst));
4193 v_decodebin[WFD_SINK_V_D_BIN].gst = NULL;
4196 wfd_sink_debug("video decodebin has parent(%s), unref it",
4197 GST_STR_NULL(GST_OBJECT_NAME(GST_OBJECT(parent))));
4199 gst_object_unref(GST_OBJECT(parent));
4203 wfd_sink->video_decodebin_is_linked = FALSE;
4205 MMWFDSINK_FREEIF(wfd_sink->pipeline->v_decodebin);
4207 wfd_sink_debug_fleave();
4209 return MM_ERROR_NONE;
4212 int __mm_wfd_sink_create_video_decodebin(mm_wfd_sink_t *wfd_sink)
4214 MMWFDSinkGstElement *v_decodebin = NULL;
4215 guint video_codec = WFD_VIDEO_UNKNOWN;
4216 GList *element_bucket = NULL;
4217 gboolean link = TRUE;
4220 wfd_sink_debug_fenter();
4222 wfd_sink_return_val_if_fail(wfd_sink &&
4224 MM_ERROR_WFD_NOT_INITIALIZED);
4226 if (wfd_sink->pipeline->v_decodebin) {
4227 wfd_sink_debug("video decodebin is already created... nothing to do");
4228 return MM_ERROR_NONE;
4231 /* check video decodebin could be linked now */
4232 switch (wfd_sink->stream_info.video_stream_info.codec) {
4233 case MM_WFD_SINK_VIDEO_CODEC_H264:
4234 video_codec = WFD_VIDEO_H264;
4237 case MM_WFD_SINK_VIDEO_CODEC_H265:
4238 video_codec = WFD_VIDEO_H265;
4241 case MM_WFD_SINK_VIDEO_CODEC_NONE:
4243 wfd_sink_debug("video decodebin could NOT be linked now, just create");
4244 video_codec = wfd_sink->ini.wfd_video_formats.video_codec | wfd_sink->ini.wfd2_video_formats.video_codec;
4250 v_decodebin = (MMWFDSinkGstElement *)g_malloc0(sizeof(MMWFDSinkGstElement) * WFD_SINK_V_D_NUM);
4252 wfd_sink_error("failed to allocate memory for video decodebin");
4253 return MM_ERROR_WFD_NO_FREE_SPACE;
4256 memset(v_decodebin, 0, sizeof(MMWFDSinkGstElement) * WFD_SINK_V_D_NUM);
4258 /* create video decodebin */
4259 v_decodebin[WFD_SINK_V_D_BIN].id = WFD_SINK_V_D_BIN;
4260 v_decodebin[WFD_SINK_V_D_BIN].gst = gst_bin_new("video_decodebin");
4261 if (!v_decodebin[WFD_SINK_V_D_BIN].gst) {
4262 wfd_sink_error("failed to create video decodebin");
4267 MMWFDSINK_CREATE_ELEMENT(v_decodebin, WFD_SINK_V_D_QUEUE, "queue", "video_queue", FALSE);
4268 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_QUEUE].gst, "sink");
4269 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_QUEUE].gst, "src");
4270 if (v_decodebin[WFD_SINK_V_D_QUEUE].gst)
4271 __mm_wfd_sink_prepare_queue(wfd_sink, v_decodebin[WFD_SINK_V_D_QUEUE].gst);
4274 MMWFDSINK_CREATE_ELEMENT(v_decodebin, WFD_SINK_V_D_HDCP, wfd_sink->ini.name_of_video_hdcp, "video_hdcp", FALSE);
4275 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_HDCP].gst, "sink");
4276 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_HDCP].gst, "src");
4278 if (video_codec & WFD_VIDEO_H264) {
4280 MMWFDSINK_CREATE_ELEMENT(v_decodebin, WFD_SINK_V_D_H264_PARSE, wfd_sink->ini.name_of_video_h264_parser, "video_h264_parser", FALSE);
4281 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_H264_PARSE].gst, "sink");
4282 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_H264_PARSE].gst, "src");
4285 MMWFDSINK_CREATE_ELEMENT(v_decodebin, WFD_SINK_V_D_H264_DEC, wfd_sink->ini.name_of_video_h264_decoder, "video_h264_dec", FALSE);
4286 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_H264_DEC].gst, "sink");
4287 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_H264_DEC].gst, "src");
4288 if (v_decodebin[WFD_SINK_V_D_H264_DEC].gst) {
4289 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_videodec(wfd_sink, v_decodebin[WFD_SINK_V_D_H264_DEC].gst)) {
4290 wfd_sink_error("failed to set video decoder property...");
4296 if (video_codec & WFD_VIDEO_H265) {
4298 MMWFDSINK_CREATE_ELEMENT(v_decodebin, WFD_SINK_V_D_H265_PARSE, wfd_sink->ini.name_of_video_h264_parser, "video_h265_parser", FALSE);
4299 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_H265_PARSE].gst, "sink");
4300 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_H265_PARSE].gst, "src");
4303 MMWFDSINK_CREATE_ELEMENT(v_decodebin, WFD_SINK_V_D_H265_DEC, wfd_sink->ini.name_of_video_h265_decoder, "video_h265_dec", FALSE);
4304 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_H265_DEC].gst, "sink");
4305 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_H265_DEC].gst, "src");
4306 if (v_decodebin[WFD_SINK_V_D_H265_DEC].gst) {
4307 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_videodec(wfd_sink, v_decodebin[WFD_SINK_V_D_H265_DEC].gst)) {
4308 wfd_sink_error("failed to set video decoder property...");
4314 g_list_free(element_bucket);
4317 wfd_sink->pipeline->v_decodebin = v_decodebin;
4319 /* link video decodebin if video codec is fixed */
4321 if (MM_ERROR_NONE != __mm_wfd_sink_link_video_decodebin(wfd_sink)) {
4322 wfd_sink_error("failed to link video decodebin, destroy video decodebin");
4323 __mm_wfd_sink_destroy_video_decodebin(wfd_sink);
4324 return MM_ERROR_WFD_INTERNAL;
4328 wfd_sink_debug_fleave();
4330 return MM_ERROR_NONE;
4334 wfd_sink_error("failed to create video decodebin, releasing all");
4336 g_list_free(element_bucket);
4338 /* release element which are not added to bin */
4339 for (i = 1; i < WFD_SINK_V_D_NUM; i++) { /* NOTE : skip bin */
4340 if (v_decodebin != NULL && v_decodebin[i].gst) {
4341 GstObject *parent = NULL;
4342 parent = gst_element_get_parent(v_decodebin[i].gst);
4345 gst_object_unref(GST_OBJECT(v_decodebin[i].gst));
4346 v_decodebin[i].gst = NULL;
4348 gst_object_unref(GST_OBJECT(parent));
4354 /* release video decodebin with it's childs */
4355 if (v_decodebin != NULL && v_decodebin[WFD_SINK_V_D_BIN].gst) {
4356 gst_object_unref(GST_OBJECT(v_decodebin[WFD_SINK_V_D_BIN].gst));
4357 v_decodebin[WFD_SINK_V_D_BIN].gst = NULL;
4360 MMWFDSINK_FREEIF(v_decodebin);
4362 return MM_ERROR_WFD_INTERNAL;
4365 static int __mm_wfd_sink_destroy_video_sinkbin(mm_wfd_sink_t *wfd_sink)
4367 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
4368 MMWFDSinkGstElement *v_sinkbin = NULL;
4369 GstObject *parent = NULL;
4372 wfd_sink_debug_fenter();
4374 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
4376 if (wfd_sink->pipeline &&
4377 wfd_sink->pipeline->v_sinkbin &&
4378 wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_BIN].gst) {
4379 v_sinkbin = wfd_sink->pipeline->v_sinkbin;
4381 wfd_sink_debug("video sinkbin is not created, nothing to destroy");
4382 return MM_ERROR_NONE;
4386 parent = gst_element_get_parent(v_sinkbin[WFD_SINK_V_S_BIN].gst);
4388 wfd_sink_debug("video sinkbin has no parent.. need to relase by itself");
4390 if (GST_STATE(v_sinkbin[WFD_SINK_V_S_BIN].gst) >= GST_STATE_READY) {
4391 wfd_sink_debug("try to change state of video sinkbin to NULL");
4392 ret = gst_element_set_state(v_sinkbin[WFD_SINK_V_S_BIN].gst, GST_STATE_NULL);
4393 if (ret != GST_STATE_CHANGE_SUCCESS) {
4394 wfd_sink_error("failed to change state of video sinkbin to NULL");
4395 return MM_ERROR_WFD_INTERNAL;
4398 /* release element which are not added to bin */
4399 for (i = 1; i < WFD_SINK_V_S_NUM; i++) { /* NOTE : skip bin */
4400 if (v_sinkbin[i].gst) {
4401 parent = gst_element_get_parent(v_sinkbin[i].gst);
4403 wfd_sink_debug("unref %s(current ref %d)",
4404 GST_STR_NULL(GST_ELEMENT_NAME(v_sinkbin[i].gst)),
4405 ((GObject *) v_sinkbin[i].gst)->ref_count);
4406 gst_object_unref(GST_OBJECT(v_sinkbin[i].gst));
4407 v_sinkbin[i].gst = NULL;
4409 wfd_sink_debug("%s has parent.(current ref %d)",
4410 GST_STR_NULL(GST_ELEMENT_NAME(v_sinkbin[i].gst)),
4411 ((GObject *) v_sinkbin[i].gst)->ref_count);
4412 gst_object_unref(GST_OBJECT(parent));
4417 /* release video sinkbin with it's childs */
4418 if (v_sinkbin[WFD_SINK_V_S_BIN].gst) {
4419 gst_object_unref(GST_OBJECT(v_sinkbin[WFD_SINK_V_S_BIN].gst));
4420 v_sinkbin[WFD_SINK_V_S_BIN].gst = NULL;
4423 wfd_sink_debug("video sinkbin has parent(%s), unref it ",
4424 GST_STR_NULL(GST_OBJECT_NAME(GST_OBJECT(parent))));
4426 gst_object_unref(GST_OBJECT(parent));
4430 MMWFDSINK_FREEIF(wfd_sink->pipeline->v_sinkbin);
4432 wfd_sink_debug_fleave();
4434 return MM_ERROR_NONE;
4437 int __mm_wfd_sink_create_video_sinkbin(mm_wfd_sink_t *wfd_sink)
4439 MMWFDSinkGstElement *first_element = NULL;
4440 MMWFDSinkGstElement *v_sinkbin = NULL;
4441 GList *element_bucket = NULL;
4443 GstPad *ghostpad = NULL;
4445 gint surface_type = MM_DISPLAY_SURFACE_OVERLAY;
4447 wfd_sink_debug_fenter();
4449 wfd_sink_return_val_if_fail(wfd_sink &&
4451 MM_ERROR_WFD_NOT_INITIALIZED);
4453 if (wfd_sink->pipeline->v_sinkbin != NULL) {
4454 wfd_sink_error("The video sink bin is already created.");
4455 return MM_ERROR_NONE;
4459 v_sinkbin = (MMWFDSinkGstElement *)g_malloc0(sizeof(MMWFDSinkGstElement) * WFD_SINK_V_S_NUM);
4461 wfd_sink_error("failed to allocate memory for video sinkbin");
4462 return MM_ERROR_WFD_NO_FREE_SPACE;
4465 memset(v_sinkbin, 0, sizeof(MMWFDSinkGstElement) * WFD_SINK_V_S_NUM);
4467 /* create video sinkbin */
4468 v_sinkbin[WFD_SINK_V_S_BIN].id = WFD_SINK_V_S_BIN;
4469 v_sinkbin[WFD_SINK_V_S_BIN].gst = gst_bin_new("video_sinkbin");
4470 if (!v_sinkbin[WFD_SINK_V_S_BIN].gst) {
4471 wfd_sink_error("failed to create video sinkbin");
4475 /* create convert */
4476 MMWFDSINK_CREATE_ELEMENT(v_sinkbin, WFD_SINK_V_S_CONVERT, wfd_sink->ini.name_of_video_converter, "video_convert", TRUE);
4477 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_sinkbin[WFD_SINK_V_S_CONVERT].gst, "sink");
4478 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_sinkbin[WFD_SINK_V_S_CONVERT].gst, "src");
4481 MMWFDSINK_CREATE_ELEMENT(v_sinkbin, WFD_SINK_V_S_FILTER, wfd_sink->ini.name_of_video_filter, "video_filter", TRUE);
4482 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_sinkbin[WFD_SINK_V_S_FILTER].gst, "sink");
4483 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_sinkbin[WFD_SINK_V_S_FILTER].gst, "src");
4484 if (v_sinkbin[WFD_SINK_V_S_FILTER].gst) {
4485 GstCaps *caps = NULL;
4486 caps = gst_caps_new_simple("video/x-raw", "format", G_TYPE_STRING, "SN12", NULL);
4487 g_object_set(G_OBJECT(v_sinkbin[WFD_SINK_V_S_FILTER].gst), "caps", caps, NULL);
4488 gst_object_unref(GST_OBJECT(caps));
4493 mm_attrs_get_int_by_name(wfd_sink->attrs, "display_surface_type", &surface_type);
4495 if (surface_type == MM_DISPLAY_SURFACE_OVERLAY) {
4496 MMWFDSINK_CREATE_ELEMENT(v_sinkbin, WFD_SINK_V_S_SINK, wfd_sink->ini.name_of_video_sink, "video_sink", TRUE);
4497 } else if (surface_type == MM_DISPLAY_SURFACE_EVAS) {
4498 MMWFDSINK_CREATE_ELEMENT(v_sinkbin, WFD_SINK_V_S_SINK, wfd_sink->ini.name_of_video_evas_sink, "video_sink", TRUE);
4500 wfd_sink_error("failed to set video sink....");
4504 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_sinkbin[WFD_SINK_V_S_SINK].gst, "sink");
4505 if (v_sinkbin[WFD_SINK_V_S_SINK].gst) {
4506 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_videosink(wfd_sink, v_sinkbin[WFD_SINK_V_S_SINK].gst)) {
4507 wfd_sink_error("failed to set video sink property....");
4512 /* adding created elements to video sinkbin */
4513 if (!__mm_wfd_sink_gst_element_add_bucket_to_bin(GST_BIN_CAST(v_sinkbin[WFD_SINK_V_S_BIN].gst), element_bucket, FALSE)) {
4514 wfd_sink_error("failed to add elements to video sinkbin");
4518 /* linking elements in the bucket by added order. */
4519 if (__mm_wfd_sink_gst_element_link_bucket(element_bucket) == -1) {
4520 wfd_sink_error("failed to link elements of the video sinkbin");
4524 /* get first element's sinkpad for creating ghostpad */
4525 first_element = (MMWFDSinkGstElement *)g_list_nth_data(element_bucket, 0);
4526 if (!first_element) {
4527 wfd_sink_error("failed to get first element of the video sinkbin");
4531 pad = gst_element_get_static_pad(GST_ELEMENT(first_element->gst), "sink");
4533 wfd_sink_error("failed to get pad from first element(%s) of the video sinkbin",
4534 GST_STR_NULL(GST_ELEMENT_NAME(first_element->gst)));
4538 ghostpad = gst_ghost_pad_new("sink", pad);
4540 wfd_sink_error("failed to create ghostpad of the video sinkbin");
4544 if (FALSE == gst_element_add_pad(v_sinkbin[WFD_SINK_V_S_BIN].gst, ghostpad)) {
4545 wfd_sink_error("failed to add ghostpad to video sinkbin");
4549 gst_object_unref(GST_OBJECT(pad));
4552 g_list_free(element_bucket);
4553 element_bucket = NULL;
4556 wfd_sink->pipeline->v_sinkbin = v_sinkbin;
4558 wfd_sink_debug_fleave();
4560 return MM_ERROR_NONE;
4564 wfd_sink_error("failed to create video sinkbin, releasing all");
4567 gst_object_unref(GST_OBJECT(pad));
4572 gst_object_unref(GST_OBJECT(ghostpad));
4576 if (element_bucket) {
4577 g_list_free(element_bucket);
4578 element_bucket = NULL;
4581 /* release element which are not added to bin */
4582 for (i = 1; i < WFD_SINK_V_S_NUM; i++) { /* NOTE : skip bin */
4583 if (v_sinkbin != NULL && v_sinkbin[i].gst) {
4584 GstObject *parent = NULL;
4585 parent = gst_element_get_parent(v_sinkbin[i].gst);
4588 gst_object_unref(GST_OBJECT(v_sinkbin[i].gst));
4589 v_sinkbin[i].gst = NULL;
4591 gst_object_unref(GST_OBJECT(parent));
4597 /* release video sinkbin with it's childs */
4598 if (v_sinkbin != NULL && v_sinkbin[WFD_SINK_V_S_BIN].gst) {
4599 gst_object_unref(GST_OBJECT(v_sinkbin[WFD_SINK_V_S_BIN].gst));
4600 v_sinkbin[WFD_SINK_V_S_BIN].gst = NULL;
4602 MMWFDSINK_FREEIF(v_sinkbin);
4604 return MM_ERROR_WFD_INTERNAL;
4607 static int __mm_wfd_sink_destroy_pipeline(mm_wfd_sink_t *wfd_sink)
4609 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
4612 wfd_sink_debug_fenter();
4614 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
4616 /* cleanup gst stuffs */
4617 if (wfd_sink->pipeline) {
4618 MMWFDSinkGstElement *mainbin = wfd_sink->pipeline->mainbin;
4622 PRINT_WFD_REF_COUNT(wfd_sink);
4624 ret = gst_element_set_state(mainbin[WFD_SINK_M_PIPE].gst, GST_STATE_NULL);
4625 if (ret != GST_STATE_CHANGE_SUCCESS) {
4626 wfd_sink_error("failed to change state of pipeline to NULL");
4627 return MM_ERROR_WFD_INTERNAL;
4629 wfd_sink_debug("Successed to change state of pipeline to NULL");
4632 bus = gst_pipeline_get_bus(GST_PIPELINE(mainbin[WFD_SINK_M_PIPE].gst));
4634 GstMessage *gst_msg = NULL;
4635 while ((gst_msg = gst_bus_pop(bus)) != NULL) {
4636 _mm_wfd_sink_msg_callback(bus, gst_msg, (gpointer)wfd_sink);
4637 gst_message_unref(gst_msg);
4640 gst_object_unref(bus);
4644 PRINT_WFD_REF_COUNT(wfd_sink);
4646 if (MM_ERROR_NONE != __mm_wfd_sink_destroy_video_decodebin(wfd_sink)) {
4647 wfd_sink_error("failed to destroy video decodebin");
4648 return MM_ERROR_WFD_INTERNAL;
4651 if (MM_ERROR_NONE != __mm_wfd_sink_destroy_audio_decodebin(wfd_sink)) {
4652 wfd_sink_error("failed to destroy audio decodebin");
4653 return MM_ERROR_WFD_INTERNAL;
4656 if (MM_ERROR_NONE != __mm_wfd_sink_destroy_video_sinkbin(wfd_sink)) {
4657 wfd_sink_error("failed to destroy video sinkbin");
4658 return MM_ERROR_WFD_INTERNAL;
4661 if (MM_ERROR_NONE != __mm_wfd_sink_destroy_audio_sinkbin(wfd_sink)) {
4662 wfd_sink_error("failed to destroy audio sinkbin");
4663 return MM_ERROR_WFD_INTERNAL;
4666 gst_object_unref(GST_OBJECT(mainbin[WFD_SINK_M_PIPE].gst));
4667 mainbin[WFD_SINK_M_PIPE].gst = NULL;
4669 MMWFDSINK_FREEIF(mainbin);
4672 MMWFDSINK_FREEIF(wfd_sink->pipeline);
4675 if (wfd_sink->msg_callback_id > 0) {
4676 g_source_remove(wfd_sink->msg_callback_id);
4677 wfd_sink->msg_callback_id = 0;
4680 wfd_sink->audio_decodebin_is_linked = FALSE;
4681 wfd_sink->video_decodebin_is_linked = FALSE;
4682 wfd_sink->need_to_reset_basetime = FALSE;
4684 wfd_sink_debug_fleave();
4686 return MM_ERROR_NONE;
4690 __mm_wfd_sink_dump_pipeline_state(mm_wfd_sink_t *wfd_sink)
4692 GstIterator *iter = NULL;
4693 gboolean done = FALSE;
4695 GstElement *item = NULL;
4696 GstElementFactory *factory = NULL;
4698 GstState state = GST_STATE_VOID_PENDING;
4699 GstState pending = GST_STATE_VOID_PENDING;
4700 GstClockTime time = 200 * GST_MSECOND;
4702 wfd_sink_debug_fenter();
4704 wfd_sink_return_if_fail(wfd_sink &&
4705 wfd_sink->pipeline &&
4706 wfd_sink->pipeline->mainbin &&
4707 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst);
4709 iter = gst_bin_iterate_recurse(GST_BIN(wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst));
4713 switch (gst_iterator_next(iter, (gpointer)&item)) {
4714 case GST_ITERATOR_OK:
4715 gst_element_get_state(GST_ELEMENT(item), &state, &pending, time);
4717 factory = gst_element_get_factory(item) ;
4719 wfd_sink_error("%s:%s : From:%s To:%s refcount : %d",
4720 GST_STR_NULL(GST_OBJECT_NAME(factory)),
4721 GST_STR_NULL(GST_ELEMENT_NAME(item)),
4722 gst_element_state_get_name(state),
4723 gst_element_state_get_name(pending),
4724 GST_OBJECT_REFCOUNT_VALUE(item));
4726 gst_object_unref(item);
4729 case GST_ITERATOR_RESYNC:
4730 gst_iterator_resync(iter);
4732 case GST_ITERATOR_ERROR:
4735 case GST_ITERATOR_DONE:
4745 item = GST_ELEMENT_CAST(wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst);
4747 gst_element_get_state(GST_ELEMENT(item), &state, &pending, time);
4749 factory = gst_element_get_factory(item) ;
4751 wfd_sink_error("%s:%s : From:%s To:%s refcount : %d",
4752 GST_OBJECT_NAME(factory),
4753 GST_ELEMENT_NAME(item),
4754 gst_element_state_get_name(state),
4755 gst_element_state_get_name(pending),
4756 GST_OBJECT_REFCOUNT_VALUE(item));
4760 gst_iterator_free(iter);
4762 wfd_sink_debug_fleave();
4768 _mm_wfds_sink_get_state_name(MMWFDSinkStateType state)
4771 case MM_WFD_SINK_STATE_NONE:
4773 case MM_WFD_SINK_STATE_NULL:
4775 case MM_WFD_SINK_STATE_PREPARED:
4777 case MM_WFD_SINK_STATE_CONNECTED:
4779 case MM_WFD_SINK_STATE_PLAYING:
4781 case MM_WFD_SINK_STATE_PAUSED:
4783 case MM_WFD_SINK_STATE_DISCONNECTED:
4784 return "DISCONNECTED";
4790 int _mm_wfd_sink_set_resolution(mm_wfd_sink_t *wfd_sink, MMWFDSinkResolution resolution)
4792 MMWFDSinkStateType cur_state = MM_WFD_SINK_STATE_NONE;
4794 wfd_sink_debug_fenter();
4796 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
4798 MMWFDSINK_PRINT_STATE(wfd_sink);
4799 cur_state = MMWFDSINK_CURRENT_STATE(wfd_sink);
4800 if (cur_state != MM_WFD_SINK_STATE_NULL) {
4801 wfd_sink_error("This function must be called when MM_WFD_SINK_STATE_NULL");
4802 return MM_ERROR_WFD_INVALID_STATE;
4805 wfd_sink->supportive_resolution = resolution;
4807 wfd_sink_debug_fleave();
4809 return MM_ERROR_NONE;
4812 void __mm_wfd_sink_print_ref_count(mm_wfd_sink_t *wfd_sink)
4815 MMWFDSinkGstElement *mainbin = NULL;
4816 MMWFDSinkGstElement *v_decodebin = NULL;
4817 MMWFDSinkGstElement *a_decodebin = NULL;
4818 MMWFDSinkGstElement *v_sinkbin = NULL;
4819 MMWFDSinkGstElement *a_sinkbin = NULL;
4821 wfd_sink_debug_fenter();
4823 wfd_sink_return_if_fail(wfd_sink);
4824 wfd_sink_return_if_fail(wfd_sink->pipeline);
4826 wfd_sink_debug("************* wfd pipeline ref count start *************");
4827 wfd_sink_debug("try to check mainbin");
4829 if (wfd_sink->pipeline->mainbin &&
4830 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst) {
4831 mainbin = wfd_sink->pipeline->mainbin;
4833 for (i = 0; i < WFD_SINK_M_NUM; i++) {
4834 if (mainbin[i].gst) {
4835 wfd_sink_debug("%s(current ref %d)",
4836 GST_ELEMENT_NAME(mainbin[i].gst),
4837 GST_OBJECT_REFCOUNT(mainbin[i].gst));
4842 wfd_sink_debug("try to check a_decodebin");
4844 if (wfd_sink->pipeline->a_decodebin &&
4845 wfd_sink->pipeline->a_decodebin[WFD_SINK_A_D_BIN].gst) {
4846 a_decodebin = wfd_sink->pipeline->a_decodebin;
4848 for (i = 0; i < WFD_SINK_A_D_NUM; i++) {
4849 if (a_decodebin[i].gst) {
4850 wfd_sink_debug("%s(current ref %d)",
4851 GST_ELEMENT_NAME(a_decodebin[i].gst),
4852 GST_OBJECT_REFCOUNT(a_decodebin[i].gst));
4857 wfd_sink_debug("try to check a_sinkbin");
4859 if (wfd_sink->pipeline->a_sinkbin &&
4860 wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_BIN].gst) {
4861 a_sinkbin = wfd_sink->pipeline->a_sinkbin;
4863 for (i = 0; i < WFD_SINK_A_S_NUM; i++) {
4864 if (a_sinkbin[i].gst) {
4865 wfd_sink_debug("%s(current ref %d)",
4866 GST_ELEMENT_NAME(a_sinkbin[i].gst),
4867 GST_OBJECT_REFCOUNT(a_sinkbin[i].gst));
4872 wfd_sink_debug("try to check v_decodebin");
4874 if (wfd_sink->pipeline->v_decodebin &&
4875 wfd_sink->pipeline->v_decodebin[WFD_SINK_V_D_BIN].gst) {
4876 v_decodebin = wfd_sink->pipeline->v_decodebin;
4878 for (i = 0; i < WFD_SINK_V_D_NUM; i++) {
4879 if (v_decodebin[i].gst) {
4880 wfd_sink_debug("%s(current ref %d)",
4881 GST_ELEMENT_NAME(v_decodebin[i].gst),
4882 GST_OBJECT_REFCOUNT(v_decodebin[i].gst));
4887 wfd_sink_debug("try to check v_sinkbin");
4889 if (wfd_sink->pipeline->v_sinkbin &&
4890 wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_BIN].gst) {
4891 v_sinkbin = wfd_sink->pipeline->v_sinkbin;
4893 for (i = 0; i < WFD_SINK_V_S_NUM; i++) {
4894 if (v_sinkbin[i].gst) {
4895 wfd_sink_debug("%s(current ref %d)",
4896 GST_ELEMENT_NAME(v_sinkbin[i].gst),
4897 GST_OBJECT_REFCOUNT(v_sinkbin[i].gst));
4901 wfd_sink_debug("************* wfd pipeline ref count end *************");
4902 wfd_sink_debug_fleave();