4 * Copyright (c) 2011 - 2013 Samsung Electronics Co., Ltd. All rights reserved.
6 * Contact: JongHyuk Choi <jhchoi.choi@samsung.com>, ByungWook Jang <bw.jang@samsung.com>,
7 * Maksym Ukhanov <m.ukhanov@samsung.com>, Hyunjun Ko <zzoon.ko@samsung.com>
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
24 #include <gst/video/videooverlay.h>
25 #include <Elementary.h>
26 #include <Ecore_Wl2.h>
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 gboolean _mm_wfd_sink_msg_callback(GstBus *bus, GstMessage *msg, gpointer data);
47 static void __mm_wfd_sink_dump_pipeline_state(mm_wfd_sink_t *wfd_sink);
49 int _mm_wfd_sink_create(mm_wfd_sink_t **wfd_sink, const char *ini_path)
51 int result = MM_ERROR_NONE;
53 wfd_sink_debug_fenter();
55 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
56 if (ini_path == NULL) {
57 ini_path = MM_WFD_SINK_INI_DEFAULT_PATH;
58 wfd_sink_debug("The wfd ini file path is set as defalut[%s]", ini_path);
61 mm_wfd_sink_t *new_wfd_sink = NULL;
64 new_wfd_sink = g_malloc0(sizeof(mm_wfd_sink_t));
66 wfd_sink_error("failed to allocate memory for wi-fi display sink");
67 return MM_ERROR_WFD_NO_FREE_SPACE;
70 /* Initialize gstreamer related */
71 new_wfd_sink->attrs = 0;
73 new_wfd_sink->pipeline = NULL;
74 new_wfd_sink->audio_decodebin_is_linked = FALSE;
75 new_wfd_sink->video_decodebin_is_linked = FALSE;
77 /* Initialize timestamp compensation related */
78 new_wfd_sink->need_to_reset_basetime = FALSE;
79 new_wfd_sink->clock = NULL;
80 new_wfd_sink->video_buffer_count = 0LL;
81 new_wfd_sink->video_average_gap = 0LL;
82 new_wfd_sink->video_accumulated_gap = 0LL;
83 new_wfd_sink->audio_buffer_count = 0LL;
84 new_wfd_sink->audio_average_gap = 0LL;
85 new_wfd_sink->audio_accumulated_gap = 0LL;
86 new_wfd_sink->last_buffer_timestamp = GST_CLOCK_TIME_NONE;
88 /* Initialize all states */
89 MMWFDSINK_CURRENT_STATE(new_wfd_sink) = MM_WFD_SINK_STATE_NONE;
90 MMWFDSINK_PREVIOUS_STATE(new_wfd_sink) = MM_WFD_SINK_STATE_NONE;
91 MMWFDSINK_PENDING_STATE(new_wfd_sink) = MM_WFD_SINK_STATE_NONE;
93 /* initialize audio/video information */
94 new_wfd_sink->stream_info.audio_stream_info.codec = MM_WFD_SINK_AUDIO_CODEC_NONE;
95 new_wfd_sink->stream_info.audio_stream_info.channels = 0;
96 new_wfd_sink->stream_info.audio_stream_info.sample_rate = 0;
97 new_wfd_sink->stream_info.audio_stream_info.bitwidth = 0;
98 new_wfd_sink->stream_info.video_stream_info.codec = MM_WFD_SINK_VIDEO_CODEC_NONE;
99 new_wfd_sink->stream_info.video_stream_info.width = 0;
100 new_wfd_sink->stream_info.video_stream_info.height = 0;
101 new_wfd_sink->stream_info.video_stream_info.frame_rate = 0;
103 /* Initialize command */
104 new_wfd_sink->cmd = MM_WFD_SINK_COMMAND_CREATE;
105 new_wfd_sink->waiting_cmd = FALSE;
107 /* Initialize manager related */
108 new_wfd_sink->manager_thread = NULL;
109 new_wfd_sink->manager_thread_cmd = NULL;
110 new_wfd_sink->manager_thread_exit = FALSE;
112 /* Initialize video resolution */
113 new_wfd_sink->supportive_resolution = MM_WFD_SINK_RESOLUTION_UNKNOWN;
115 /* construct attributes */
116 new_wfd_sink->attrs = _mmwfd_construct_attribute((MMHandleType)new_wfd_sink);
117 if (!new_wfd_sink->attrs) {
118 MMWFDSINK_FREEIF(new_wfd_sink);
119 wfd_sink_error("failed to set attribute");
120 return MM_ERROR_WFD_INTERNAL;
123 /* load ini for initialize */
124 result = mm_wfd_sink_ini_load(&new_wfd_sink->ini, ini_path);
125 if (result != MM_ERROR_NONE) {
126 wfd_sink_error("failed to load ini file[%s]", ini_path);
127 goto fail_to_load_ini;
129 new_wfd_sink->need_to_reset_basetime = new_wfd_sink->ini.enable_reset_basetime;
131 /* initialize manager */
132 result = _mm_wfd_sink_init_manager(new_wfd_sink);
133 if (result < MM_ERROR_NONE) {
134 wfd_sink_error("failed to init manager : %d", result);
138 /* initialize gstreamer */
139 result = __mm_wfd_sink_init_gstreamer(new_wfd_sink);
140 if (result < MM_ERROR_NONE) {
141 wfd_sink_error("failed to init gstreamer : %d", result);
146 __mm_wfd_sink_set_state(new_wfd_sink, MM_WFD_SINK_STATE_NULL);
148 /* now take handle */
149 *wfd_sink = new_wfd_sink;
151 wfd_sink_debug_fleave();
157 mm_wfd_sink_ini_unload(&new_wfd_sink->ini);
159 _mmwfd_deconstruct_attribute(new_wfd_sink->attrs);
160 MMWFDSINK_FREEIF(new_wfd_sink);
167 int _mm_wfd_sink_prepare(mm_wfd_sink_t *wfd_sink)
169 int result = MM_ERROR_NONE;
171 wfd_sink_debug_fenter();
173 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
175 /* check current wi-fi display sink state */
176 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_PREPARE);
178 /* construct pipeline */
179 /* create main pipeline */
180 result = __mm_wfd_sink_create_pipeline(wfd_sink);
181 if (result < MM_ERROR_NONE) {
182 wfd_sink_error("failed to create pipeline : %d", result);
186 /* create video decodebin */
187 result = __mm_wfd_sink_create_video_decodebin(wfd_sink);
188 if (result < MM_ERROR_NONE) {
189 wfd_sink_error("failed to create video decodebin %d", result);
193 /* create video sinkbin */
194 result = __mm_wfd_sink_create_video_sinkbin(wfd_sink);
195 if (result < MM_ERROR_NONE) {
196 wfd_sink_error("failed to create video sinkbin %d", result);
200 /* create audio decodebin */
201 result = __mm_wfd_sink_create_audio_decodebin(wfd_sink);
202 if (result < MM_ERROR_NONE) {
203 wfd_sink_error("fail to create audio decodebin : %d", result);
207 /* create audio sinkbin */
208 result = __mm_wfd_sink_create_audio_sinkbin(wfd_sink);
209 if (result < MM_ERROR_NONE) {
210 wfd_sink_error("fail to create audio sinkbin : %d", result);
214 /* set pipeline READY state */
215 result = __mm_wfd_sink_set_pipeline_state(wfd_sink, GST_STATE_READY, TRUE);
216 if (result < MM_ERROR_NONE) {
217 wfd_sink_error("failed to set state : %d", result);
222 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_PREPARED);
224 wfd_sink_debug_fleave();
230 /* need to destroy pipeline already created */
231 __mm_wfd_sink_destroy_pipeline(wfd_sink);
235 int _mm_wfd_sink_connect(mm_wfd_sink_t *wfd_sink, const char *uri)
237 int result = MM_ERROR_NONE;
239 wfd_sink_debug_fenter();
241 wfd_sink_return_val_if_fail(uri && strlen(uri) > strlen("rtsp://"),
242 MM_ERROR_WFD_INVALID_ARGUMENT);
243 wfd_sink_return_val_if_fail(wfd_sink &&
244 wfd_sink->pipeline &&
245 wfd_sink->pipeline->mainbin &&
246 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst &&
247 wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst &&
248 wfd_sink->pipeline->mainbin[WFD_SINK_M_DEPAY].gst &&
249 wfd_sink->pipeline->mainbin[WFD_SINK_M_DEMUX].gst,
250 MM_ERROR_WFD_NOT_INITIALIZED);
252 /* check current wi-fi display sink state */
253 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_CONNECT);
255 wfd_sink_debug("try to connect to %s.....", GST_STR_NULL(uri));
257 /* set uri to wfdsrc */
258 g_object_set(G_OBJECT(wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst), "location", uri, NULL);
260 /* set pipeline PAUSED state */
261 result = __mm_wfd_sink_set_pipeline_state(wfd_sink, GST_STATE_PAUSED, TRUE);
262 if (result < MM_ERROR_NONE) {
263 wfd_sink_error("failed to set state : %d", result);
267 wfd_sink_debug_fleave();
272 int _mm_wfd_sink_start(mm_wfd_sink_t *wfd_sink)
274 int result = MM_ERROR_NONE;
276 wfd_sink_debug_fenter();
278 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
280 /* check current wi-fi display sink state */
281 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_START);
283 WFD_SINK_MANAGER_LOCK(wfd_sink) ;
284 wfd_sink_debug("check pipeline is ready to start");
285 WFD_SINK_MANAGER_UNLOCK(wfd_sink);
287 result = __mm_wfd_sink_set_pipeline_state(wfd_sink, GST_STATE_PLAYING, TRUE);
288 if (result < MM_ERROR_NONE) {
289 wfd_sink_error("failed to set state : %d", result);
293 wfd_sink_debug_fleave();
298 int _mm_wfd_sink_pause(mm_wfd_sink_t *wfd_sink)
300 int result = MM_ERROR_NONE;
302 wfd_sink_debug_fenter();
304 wfd_sink_return_val_if_fail(wfd_sink &&
305 wfd_sink->pipeline &&
306 wfd_sink->pipeline->mainbin &&
307 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst &&
308 wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst,
309 MM_ERROR_WFD_NOT_INITIALIZED);
311 /* check current wi-fi display sink state */
312 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_PAUSE);
314 g_signal_emit_by_name(wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst, "request-pause", NULL);
316 wfd_sink_debug_fleave();
321 int _mm_wfd_sink_resume(mm_wfd_sink_t *wfd_sink)
323 int result = MM_ERROR_NONE;
325 wfd_sink_debug_fenter();
327 wfd_sink_return_val_if_fail(wfd_sink &&
328 wfd_sink->pipeline &&
329 wfd_sink->pipeline->mainbin &&
330 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst &&
331 wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst,
332 MM_ERROR_WFD_NOT_INITIALIZED);
334 /* check current wi-fi display sink state */
335 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_RESUME);
337 g_signal_emit_by_name(wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst, "request-resume", NULL);
339 wfd_sink_debug_fleave();
344 int _mm_wfd_sink_disconnect(mm_wfd_sink_t *wfd_sink)
346 int result = MM_ERROR_NONE;
348 wfd_sink_debug_fenter();
350 wfd_sink_return_val_if_fail(wfd_sink &&
351 wfd_sink->pipeline &&
352 wfd_sink->pipeline->mainbin &&
353 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst &&
354 wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst,
355 MM_ERROR_WFD_NOT_INITIALIZED);
357 /* check current wi-fi display sink state */
358 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_DISCONNECT);
360 WFD_SINK_MANAGER_APPEND_CMD(wfd_sink, WFD_SINK_MANAGER_CMD_EXIT);
361 WFD_SINK_MANAGER_SIGNAL_CMD(wfd_sink);
363 g_signal_emit_by_name(wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst, "request-close", NULL);
365 wfd_sink_debug_fleave();
370 int _mm_wfd_sink_unprepare(mm_wfd_sink_t *wfd_sink)
372 int result = MM_ERROR_NONE;
374 wfd_sink_debug_fenter();
376 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
378 /* check current wi-fi display sink state */
379 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_UNPREPARE);
381 WFD_SINK_MANAGER_APPEND_CMD(wfd_sink, WFD_SINK_MANAGER_CMD_EXIT);
382 WFD_SINK_MANAGER_SIGNAL_CMD(wfd_sink);
384 /* release pipeline */
385 result = __mm_wfd_sink_destroy_pipeline(wfd_sink);
386 if (result != MM_ERROR_NONE) {
387 wfd_sink_error("failed to destory pipeline");
388 return MM_ERROR_WFD_INTERNAL;
390 wfd_sink_debug("success to destory pipeline");
394 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_NULL);
396 wfd_sink_debug_fleave();
401 int _mm_wfd_sink_destroy(mm_wfd_sink_t *wfd_sink)
403 int result = MM_ERROR_NONE;
405 wfd_sink_debug_fenter();
407 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
409 /* check current wi-fi display sink state */
410 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_DESTROY);
413 mm_wfd_sink_ini_unload(&wfd_sink->ini);
415 /* release attributes */
416 _mmwfd_deconstruct_attribute(wfd_sink->attrs);
418 /* release manager thread */
419 if (MM_ERROR_NONE != _mm_wfd_sink_release_manager(wfd_sink)) {
420 wfd_sink_error("failed to release manager");
421 return MM_ERROR_WFD_INTERNAL;
425 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_NONE);
427 wfd_sink_debug_fleave();
432 int _mm_wfd_set_message_callback(mm_wfd_sink_t *wfd_sink, MMWFDMessageCallback callback, void *user_data)
434 int result = MM_ERROR_NONE;
436 wfd_sink_debug_fenter();
438 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
440 wfd_sink->msg_cb = callback;
441 wfd_sink->msg_user_data = user_data;
443 wfd_sink_debug_fleave();
448 static int __mm_wfd_sink_init_gstreamer(mm_wfd_sink_t *wfd_sink)
450 int result = MM_ERROR_NONE;
453 static const int max_argc = 50;
457 wfd_sink_debug_fenter();
460 argc = calloc(1, sizeof(gint));
461 argv = calloc(max_argc, sizeof(gchar *));
462 if (!argc || !argv) {
463 wfd_sink_error("failed to allocate memory for wfdsink");
465 MMWFDSINK_FREEIF(argv);
466 MMWFDSINK_FREEIF(argc);
468 return MM_ERROR_WFD_NO_FREE_SPACE;
471 /* we would not do fork for scanning plugins */
472 argv[*argc] = g_strdup("--gst-disable-registry-fork");
475 /* check disable registry scan */
476 argv[*argc] = g_strdup("--gst-disable-registry-update");
479 /* check disable segtrap */
480 argv[*argc] = g_strdup("--gst-disable-segtrap");
484 for (i = 0; i < 5; i++) {
485 if (strlen(wfd_sink->ini.gst_param[i]) > 2) {
486 wfd_sink_debug("set %s", wfd_sink->ini.gst_param[i]);
487 argv[*argc] = g_strdup(wfd_sink->ini.gst_param[i]);
492 wfd_sink_debug("initializing gstreamer with following parameter");
493 wfd_sink_debug("argc : %d", *argc);
495 for (i = 0; i < *argc; i++)
496 wfd_sink_debug("argv[%d] : %s", i, argv[i]);
498 /* initializing gstreamer */
499 if (!gst_init_check(argc, &argv, &err)) {
500 wfd_sink_error("failed to initialize gstreamer: %s",
501 err ? err->message : "unknown error occurred");
505 result = MM_ERROR_WFD_INTERNAL;
509 for (i = 0; i < *argc; i++)
510 MMWFDSINK_FREEIF(argv[i]);
512 MMWFDSINK_FREEIF(argv);
513 MMWFDSINK_FREEIF(argc);
515 wfd_sink_debug_fleave();
520 static GstBusSyncReply
521 _mm_wfd_bus_sync_callback(GstBus *bus, GstMessage *message, gpointer data)
523 GstBusSyncReply ret = GST_BUS_PASS;
525 wfd_sink_return_val_if_fail(message &&
526 GST_IS_MESSAGE(message) &&
527 GST_MESSAGE_SRC(message),
530 wfd_sink_debug("get message %p, %s from %p, %s", message,
531 GST_MESSAGE_TYPE_NAME(message), GST_MESSAGE_SRC(message), GST_MESSAGE_SRC_NAME(message));
533 switch (GST_MESSAGE_TYPE(message)) {
534 case GST_MESSAGE_TAG:
536 case GST_MESSAGE_DURATION:
538 case GST_MESSAGE_STATE_CHANGED:
539 /* we only handle state change messages from pipeline */
540 if (GST_IS_PIPELINE(GST_MESSAGE_SRC(message)))
541 _mm_wfd_sink_msg_callback(bus, message, data);
545 case GST_MESSAGE_ASYNC_DONE:
546 if (GST_IS_PIPELINE(GST_MESSAGE_SRC(message)))
547 _mm_wfd_sink_msg_callback(bus, message, data);
555 if (ret == GST_BUS_DROP) {
556 gst_message_unref(message);
563 int __mm_wfd_sink_activate_audio_stream(mm_wfd_sink_t *wfd_sink)
565 GstElement *valve = NULL;
566 GstPad *sinkpad = NULL;
567 GstPad *srcpad = NULL;
568 int result = MM_ERROR_NONE;
570 wfd_sink_debug_fenter();
572 wfd_sink_return_val_if_fail(wfd_sink &&
573 wfd_sink->pipeline &&
574 wfd_sink->pipeline->mainbin &&
575 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst &&
576 wfd_sink->pipeline->mainbin[WFD_SINK_M_A_VALVE].gst,
577 MM_ERROR_WFD_NOT_INITIALIZED);
579 valve = wfd_sink->pipeline->mainbin[WFD_SINK_M_A_VALVE].gst;
580 srcpad = gst_element_get_static_pad(valve, "src");
582 wfd_sink_error("failed to get src pad from %s", GST_ELEMENT_NAME(valve));
586 if (gst_pad_is_linked(srcpad)) {
587 wfd_sink_debug("%s:%s is already linked",
588 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad));
592 result = __mm_wfd_sink_prepare_audio_pipeline(wfd_sink, &sinkpad);
593 if (MM_ERROR_NONE != result) {
594 wfd_sink_error("failed to prepare audio pipeline....");
598 wfd_sink_debug("try to link %s:%s and %s:%s",
599 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
600 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
601 if (GST_PAD_LINK_OK != gst_pad_link_full(srcpad, sinkpad, GST_PAD_LINK_CHECK_NOTHING)) {
602 wfd_sink_error("failed to link %s:%s and %s:%s",
603 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
604 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
608 if (sinkpad != NULL) {
609 gst_object_unref(GST_OBJECT(sinkpad));
614 if (srcpad != NULL) {
615 gst_object_unref(GST_OBJECT(srcpad));
618 /* drop all the audio buffers using valve */
619 g_object_set(G_OBJECT(valve), "drop", FALSE, NULL);
621 wfd_sink_debug_fleave();
623 return MM_ERROR_NONE;
626 if (srcpad != NULL) {
627 gst_object_unref(GST_OBJECT(srcpad));
631 if (sinkpad != NULL) {
632 gst_object_unref(GST_OBJECT(sinkpad));
636 wfd_sink_debug_fleave();
637 return MM_ERROR_WFD_INTERNAL;
640 int __mm_wfd_sink_activate_video_stream(mm_wfd_sink_t *wfd_sink)
642 GstElement *valve = NULL;
643 GstPad *sinkpad = NULL;
644 GstPad *srcpad = NULL;
645 int result = MM_ERROR_NONE;
647 wfd_sink_debug_fenter();
649 wfd_sink_return_val_if_fail(wfd_sink &&
650 wfd_sink->pipeline &&
651 wfd_sink->pipeline->mainbin &&
652 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst &&
653 wfd_sink->pipeline->mainbin[WFD_SINK_M_V_VALVE].gst,
654 MM_ERROR_WFD_NOT_INITIALIZED);
656 valve = wfd_sink->pipeline->mainbin[WFD_SINK_M_V_VALVE].gst;
657 srcpad = gst_element_get_static_pad(valve, "src");
659 wfd_sink_error("failed to get src pad from %s", GST_ELEMENT_NAME(valve));
663 if (gst_pad_is_linked(srcpad)) {
664 wfd_sink_debug("%s:%s is already linked",
665 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad));
669 result = __mm_wfd_sink_prepare_video_pipeline(wfd_sink, &sinkpad);
670 if (MM_ERROR_NONE != result) {
671 wfd_sink_error("failed to prepare video pipeline....");
675 wfd_sink_debug("try to link %s:%s and %s:%s",
676 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
677 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
678 if (GST_PAD_LINK_OK != gst_pad_link_full(srcpad, sinkpad, GST_PAD_LINK_CHECK_NOTHING)) {
679 wfd_sink_error("failed to link %s:%s and %s:%s",
680 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
681 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
684 if (sinkpad != NULL) {
685 gst_object_unref(GST_OBJECT(sinkpad));
690 if (srcpad != NULL) {
691 gst_object_unref(GST_OBJECT(srcpad));
694 /* drop all the video buffers using valve */
695 g_object_set(G_OBJECT(valve), "drop", FALSE, NULL);
697 wfd_sink_debug_fleave();
699 return MM_ERROR_NONE;
702 if (srcpad != NULL) {
703 gst_object_unref(GST_OBJECT(srcpad));
707 if (sinkpad != NULL) {
708 gst_object_unref(GST_OBJECT(sinkpad));
712 wfd_sink_debug_fleave();
713 return MM_ERROR_WFD_INTERNAL;
716 int __mm_wfd_sink_deactivate_audio_stream(mm_wfd_sink_t *wfd_sink, gboolean unprepare)
718 int ret = MM_ERROR_NONE;
720 wfd_sink_debug_fenter();
722 wfd_sink_return_val_if_fail(wfd_sink &&
723 wfd_sink->pipeline &&
724 wfd_sink->pipeline->mainbin &&
725 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst &&
726 wfd_sink->pipeline->mainbin[WFD_SINK_M_A_VALVE].gst,
727 MM_ERROR_WFD_NOT_INITIALIZED);
729 /* drop all the audio buffers using valve */
730 g_object_set(G_OBJECT(wfd_sink->pipeline->mainbin[WFD_SINK_M_A_VALVE].gst), "drop", TRUE, NULL);
733 /* unprepare audio pipeline */
734 ret = __mm_wfd_sink_unprepare_audio_pipeline(wfd_sink);
735 if (ret != MM_ERROR_NONE) {
736 wfd_sink_error("failed to unprepare audio pipeline...");
737 return MM_ERROR_WFD_INTERNAL;
741 wfd_sink_debug_fleave();
743 return MM_ERROR_NONE;
746 int __mm_wfd_sink_deactivate_video_stream(mm_wfd_sink_t *wfd_sink, gboolean unprepare)
748 int ret = MM_ERROR_NONE;
750 wfd_sink_debug_fenter();
752 wfd_sink_return_val_if_fail(wfd_sink &&
753 wfd_sink->pipeline &&
754 wfd_sink->pipeline->mainbin &&
755 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst &&
756 wfd_sink->pipeline->mainbin[WFD_SINK_M_V_VALVE].gst,
757 MM_ERROR_WFD_NOT_INITIALIZED);
759 /* drop all the video buffers using valve */
760 g_object_set(G_OBJECT(wfd_sink->pipeline->mainbin[WFD_SINK_M_V_VALVE].gst), "drop", TRUE, NULL);
763 /* unprepare video pipeline */
764 ret = __mm_wfd_sink_unprepare_video_pipeline(wfd_sink);
765 if (ret != MM_ERROR_NONE) {
766 wfd_sink_error("failed to unprepare video pipeline...");
767 return MM_ERROR_WFD_INTERNAL;
771 wfd_sink_debug_fleave();
773 return MM_ERROR_NONE;
778 _mm_wfd_sink_msg_callback(GstBus *bus, GstMessage *msg, gpointer data)
780 mm_wfd_sink_t *wfd_sink = (mm_wfd_sink_t *) data;
781 const GstStructure *message_structure = gst_message_get_structure(msg);
783 gchar *getname = NULL;
785 wfd_sink_return_val_if_fail(wfd_sink, FALSE);
786 wfd_sink_return_val_if_fail(msg && GST_IS_MESSAGE(msg), FALSE);
788 wfd_sink_debug("got %s(%d) from %s",
789 GST_STR_NULL(GST_MESSAGE_TYPE_NAME(msg)),
790 GST_MESSAGE_TYPE(msg),
791 GST_STR_NULL(GST_OBJECT_NAME(GST_MESSAGE_SRC(msg))));
793 switch (GST_MESSAGE_TYPE(msg)) {
794 case GST_MESSAGE_ERROR: {
795 GError *error = NULL;
799 gst_message_parse_error(msg, &error, &debug);
801 wfd_sink_error("error : %s", error->message);
802 wfd_sink_error("debug : %s", debug);
804 MMWFDSINK_FREEIF(debug);
809 case GST_MESSAGE_WARNING: {
811 GError *error = NULL;
813 gst_message_parse_warning(msg, &error, &debug);
815 wfd_sink_error("warning : %s", error->message);
816 wfd_sink_error("debug : %s", debug);
818 MMWFDSINK_FREEIF(debug);
823 case GST_MESSAGE_STATE_CHANGED: {
824 const GValue *voldstate, *vnewstate, *vpending;
825 GstState oldstate, newstate, pending;
826 const GstStructure *structure;
828 /* get state info from msg */
829 structure = gst_message_get_structure(msg);
830 if (structure == NULL)
833 voldstate = gst_structure_get_value(structure, "old-state");
834 vnewstate = gst_structure_get_value(structure, "new-state");
835 vpending = gst_structure_get_value(structure, "pending-state");
836 if (voldstate == NULL || vnewstate == NULL || vpending == NULL)
839 oldstate = (GstState)voldstate->data[0].v_int;
840 newstate = (GstState)vnewstate->data[0].v_int;
841 pending = (GstState)vpending->data[0].v_int;
843 wfd_sink_debug("state changed [%s] : %s--->%s final : %s",
844 GST_OBJECT_NAME(GST_MESSAGE_SRC(msg)),
845 gst_element_state_get_name((GstState)oldstate),
846 gst_element_state_get_name((GstState)newstate),
847 gst_element_state_get_name((GstState)pending));
849 /* we only handle messages from pipeline */
850 if (msg->src != (GstObject *)wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst)
853 if (oldstate == newstate) {
854 wfd_sink_debug("pipeline reports state transition to old state");
859 case GST_STATE_VOID_PENDING:
861 case GST_STATE_READY:
862 case GST_STATE_PAUSED:
863 case GST_STATE_PLAYING:
870 case GST_MESSAGE_CLOCK_LOST: {
871 GstClock *clock = NULL;
872 gst_message_parse_clock_lost(msg, &clock);
873 wfd_sink_debug("The current clock[%s] as selected by the pipeline became unusable.",
874 (clock ? GST_OBJECT_NAME(clock) : "NULL"));
878 case GST_MESSAGE_NEW_CLOCK: {
879 GstClock *clock = NULL;
880 gst_message_parse_new_clock(msg, &clock);
884 if (wfd_sink->clock) {
885 if (wfd_sink->clock != clock)
886 wfd_sink_debug("clock is changed! [%s] -->[%s]",
887 GST_STR_NULL(GST_OBJECT_NAME(wfd_sink->clock)),
888 GST_STR_NULL(GST_OBJECT_NAME(clock)));
890 wfd_sink_debug("same clock is selected again! [%s]",
891 GST_STR_NULL(GST_OBJECT_NAME(clock)));
893 wfd_sink_debug("new clock [%s] was selected in the pipeline",
894 (GST_STR_NULL(GST_OBJECT_NAME(clock))));
897 wfd_sink->clock = clock;
901 case GST_MESSAGE_APPLICATION: {
902 const gchar *message_structure_name;
904 message_structure_name = gst_structure_get_name(message_structure);
905 if (!message_structure_name)
908 wfd_sink_debug("message name : %s", GST_STR_NULL(message_structure_name));
912 case GST_MESSAGE_ELEMENT: {
913 const gchar *structure_name = NULL;
915 structure_name = gst_structure_get_name(message_structure);
916 if (structure_name) {
917 wfd_sink_debug("got element specific message[%s]", GST_STR_NULL(structure_name));
918 if (g_strrstr(structure_name, "GstUDPSrcTimeout")) {
919 wfd_sink_error("Got %s, post error message", GST_STR_NULL(structure_name));
920 MMWFDSINK_POST_MESSAGE(wfd_sink,
921 MM_ERROR_WFD_INTERNAL,
922 MMWFDSINK_CURRENT_STATE(wfd_sink));
923 } else if (g_strrstr(structure_name, "GstWFDSessionTimeout")) {
924 wfd_sink_error("Got %s, post error message", GST_STR_NULL(structure_name));
925 MMWFDSINK_POST_MESSAGE(wfd_sink,
926 MM_ERROR_WFD_INTERNAL,
927 MM_WFD_SINK_STATE_DISCONNECTED);
933 case GST_MESSAGE_PROGRESS: {
934 GstProgressType type = GST_PROGRESS_TYPE_ERROR;
935 gchar *category = NULL, *text = NULL;
937 gst_message_parse_progress(msg, &type, &category, &text);
938 wfd_sink_debug("%s : %s ", GST_STR_NULL(category), GST_STR_NULL(text));
941 case GST_PROGRESS_TYPE_START:
943 case GST_PROGRESS_TYPE_COMPLETE:
944 if (category && !strcmp(category, "open")) {
945 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_CONNECTED);
946 } else if (category && !strcmp(category, "play")) {
947 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_PLAYING);
948 /*_mm_wfd_sink_correct_pipeline_latency (wfd_sink); */
949 } else if (category && !strcmp(category, "pause")) {
950 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_PAUSED);
951 } else if (category && !strcmp(category, "close")) {
952 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_DISCONNECTED);
955 case GST_PROGRESS_TYPE_CANCELED:
957 case GST_PROGRESS_TYPE_ERROR:
958 if (category && !strcmp(category, "open")) {
959 wfd_sink_error("got error : %s", GST_STR_NULL(text));
960 /*_mm_wfd_sink_disconnect (wfd_sink); */
961 MMWFDSINK_POST_MESSAGE(wfd_sink,
962 MM_ERROR_WFD_INTERNAL,
963 MMWFDSINK_CURRENT_STATE(wfd_sink));
964 } else if (category && !strcmp(category, "play")) {
965 wfd_sink_error("got error : %s", GST_STR_NULL(text));
966 /*_mm_wfd_sink_disconnect (wfd_sink); */
967 MMWFDSINK_POST_MESSAGE(wfd_sink,
968 MM_ERROR_WFD_INTERNAL,
969 MMWFDSINK_CURRENT_STATE(wfd_sink));
970 } else if (category && !strcmp(category, "pause")) {
971 wfd_sink_error("got error : %s", GST_STR_NULL(text));
972 /*_mm_wfd_sink_disconnect (wfd_sink); */
973 MMWFDSINK_POST_MESSAGE(wfd_sink,
974 MM_ERROR_WFD_INTERNAL,
975 MMWFDSINK_CURRENT_STATE(wfd_sink));
976 } else if (category && !strcmp(category, "close")) {
977 wfd_sink_error("got error : %s", GST_STR_NULL(text));
978 /*_mm_wfd_sink_disconnect (wfd_sink); */
979 MMWFDSINK_POST_MESSAGE(wfd_sink,
980 MM_ERROR_WFD_INTERNAL,
981 MMWFDSINK_CURRENT_STATE(wfd_sink));
983 wfd_sink_error("got error : %s", GST_STR_NULL(text));
987 wfd_sink_error("progress message has no type");
991 MMWFDSINK_FREEIF(category);
992 MMWFDSINK_FREEIF(text);
996 case GST_MESSAGE_ASYNC_START:
997 getname = gst_element_get_name(GST_MESSAGE_SRC(msg));
998 wfd_sink_debug("GST_MESSAGE_ASYNC_START : %s", getname);
999 MMWFDSINK_FREEIF(getname);
1001 case GST_MESSAGE_ASYNC_DONE:
1002 getname = gst_element_get_name(GST_MESSAGE_SRC(msg));
1003 wfd_sink_debug("GST_MESSAGE_ASYNC_DONE : %s", getname);
1004 MMWFDSINK_FREEIF(getname);
1006 case GST_MESSAGE_UNKNOWN:
1007 case GST_MESSAGE_INFO:
1008 case GST_MESSAGE_TAG:
1009 case GST_MESSAGE_BUFFERING:
1010 case GST_MESSAGE_EOS:
1011 case GST_MESSAGE_STATE_DIRTY:
1012 case GST_MESSAGE_STEP_DONE:
1013 case GST_MESSAGE_CLOCK_PROVIDE:
1014 case GST_MESSAGE_STRUCTURE_CHANGE:
1015 case GST_MESSAGE_STREAM_STATUS:
1016 case GST_MESSAGE_SEGMENT_START:
1017 case GST_MESSAGE_SEGMENT_DONE:
1018 case GST_MESSAGE_DURATION:
1019 case GST_MESSAGE_LATENCY:
1020 case GST_MESSAGE_REQUEST_STATE:
1021 case GST_MESSAGE_STEP_START:
1022 case GST_MESSAGE_QOS:
1023 case GST_MESSAGE_ANY:
1026 wfd_sink_debug("unhandled message");
1034 __mm_wfd_sink_gst_element_add_bucket_to_bin(GstBin *bin, GList *element_bucket, gboolean need_prepare)
1036 GList *bucket = element_bucket;
1037 MMWFDSinkGstElement *element = NULL;
1038 int successful_add_count = 0;
1040 wfd_sink_debug_fenter();
1042 wfd_sink_return_val_if_fail(element_bucket, 0);
1043 wfd_sink_return_val_if_fail(bin, 0);
1045 for (; bucket; bucket = bucket->next) {
1046 element = (MMWFDSinkGstElement *)bucket->data;
1048 if (element && element->gst) {
1050 gst_element_set_state(GST_ELEMENT(element->gst), GST_STATE_READY);
1052 if (!gst_bin_add(GST_BIN(bin), GST_ELEMENT(element->gst))) {
1053 wfd_sink_error("failed to add element [%s] to bin [%s]",
1054 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT(element->gst))),
1055 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT_CAST(bin))));
1059 wfd_sink_debug("add element [%s] to bin [%s]",
1060 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT(element->gst))),
1061 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT_CAST(bin))));
1063 successful_add_count++;
1067 wfd_sink_debug_fleave();
1069 return successful_add_count;
1073 __mm_wfd_sink_gst_element_link_bucket(GList *element_bucket)
1075 GList *bucket = element_bucket;
1076 MMWFDSinkGstElement *element = NULL;
1077 MMWFDSinkGstElement *prv_element = NULL;
1078 gint successful_link_count = 0;
1080 wfd_sink_debug_fenter();
1082 wfd_sink_return_val_if_fail(element_bucket, -1);
1084 prv_element = (MMWFDSinkGstElement *)bucket->data;
1085 bucket = bucket->next;
1087 for (; bucket; bucket = bucket->next) {
1088 element = (MMWFDSinkGstElement *)bucket->data;
1090 if (element && element->gst) {
1091 if (gst_element_link(GST_ELEMENT(prv_element->gst), GST_ELEMENT(element->gst))) {
1092 wfd_sink_debug("linking [%s] to [%s] success",
1093 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT(prv_element->gst))),
1094 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT(element->gst))));
1095 successful_link_count++;
1097 wfd_sink_error("linking [%s] to [%s] failed",
1098 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT(prv_element->gst))),
1099 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT(element->gst))));
1104 prv_element = element;
1107 wfd_sink_debug_fleave();
1109 return successful_link_count;
1113 __mm_wfd_sink_check_state(mm_wfd_sink_t *wfd_sink, MMWFDSinkCommandType cmd)
1115 MMWFDSinkStateType cur_state = MM_WFD_SINK_STATE_NONE;
1117 wfd_sink_debug_fenter();
1119 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
1121 MMWFDSINK_PRINT_STATE(wfd_sink);
1123 cur_state = MMWFDSINK_CURRENT_STATE(wfd_sink);
1126 case MM_WFD_SINK_COMMAND_CREATE:
1127 if (cur_state != MM_WFD_SINK_STATE_NONE)
1130 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_NULL;
1133 case MM_WFD_SINK_COMMAND_PREPARE:
1134 if (cur_state == MM_WFD_SINK_STATE_PREPARED)
1136 else if (cur_state != MM_WFD_SINK_STATE_NULL)
1139 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_PREPARED;
1142 case MM_WFD_SINK_COMMAND_CONNECT:
1143 if (cur_state == MM_WFD_SINK_STATE_CONNECTED)
1145 else if (cur_state != MM_WFD_SINK_STATE_PREPARED)
1148 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_CONNECTED;
1151 case MM_WFD_SINK_COMMAND_START:
1152 if (cur_state == MM_WFD_SINK_STATE_PLAYING)
1154 else if (cur_state != MM_WFD_SINK_STATE_CONNECTED)
1157 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_PLAYING;
1160 case MM_WFD_SINK_COMMAND_PAUSE:
1161 if (cur_state == MM_WFD_SINK_STATE_PAUSED)
1163 else if (cur_state != MM_WFD_SINK_STATE_PLAYING)
1166 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_PAUSED;
1169 case MM_WFD_SINK_COMMAND_RESUME:
1170 if (cur_state == MM_WFD_SINK_STATE_PLAYING)
1172 else if (cur_state != MM_WFD_SINK_STATE_PAUSED)
1175 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_PLAYING;
1178 case MM_WFD_SINK_COMMAND_DISCONNECT:
1179 if (cur_state == MM_WFD_SINK_STATE_NONE ||
1180 cur_state == MM_WFD_SINK_STATE_NULL ||
1181 cur_state == MM_WFD_SINK_STATE_PREPARED ||
1182 cur_state == MM_WFD_SINK_STATE_DISCONNECTED)
1184 else if (cur_state != MM_WFD_SINK_STATE_PLAYING &&
1185 cur_state != MM_WFD_SINK_STATE_CONNECTED &&
1186 cur_state != MM_WFD_SINK_STATE_PAUSED)
1189 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_DISCONNECTED;
1192 case MM_WFD_SINK_COMMAND_UNPREPARE:
1193 if (cur_state == MM_WFD_SINK_STATE_NONE ||
1194 cur_state == MM_WFD_SINK_STATE_NULL)
1197 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_NULL;
1200 case MM_WFD_SINK_COMMAND_DESTROY:
1201 if (cur_state == MM_WFD_SINK_STATE_NONE)
1204 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_NONE;
1211 wfd_sink->cmd = cmd;
1213 wfd_sink_debug_fleave();
1215 return MM_ERROR_NONE;
1218 wfd_sink_debug("already %s state, nothing to do.", MMWFDSINK_STATE_GET_NAME(cur_state));
1219 return MM_ERROR_WFD_NO_OP;
1223 wfd_sink_error("current state[%s] is invalid.", MMWFDSINK_STATE_GET_NAME(cur_state));
1224 return MM_ERROR_WFD_INVALID_STATE;
1227 int __mm_wfd_sink_set_state(mm_wfd_sink_t *wfd_sink, MMWFDSinkStateType state)
1229 wfd_sink_debug_fenter();
1231 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
1233 if (MMWFDSINK_CURRENT_STATE(wfd_sink) == state) {
1234 wfd_sink_error("already state(%s)", MMWFDSINK_STATE_GET_NAME(state));
1235 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_NONE;
1236 return MM_ERROR_NONE;
1239 /* update wi-fi display state */
1240 MMWFDSINK_PREVIOUS_STATE(wfd_sink) = MMWFDSINK_CURRENT_STATE(wfd_sink);
1241 MMWFDSINK_CURRENT_STATE(wfd_sink) = state;
1243 if (MMWFDSINK_CURRENT_STATE(wfd_sink) == MMWFDSINK_PENDING_STATE(wfd_sink))
1244 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_NONE;
1246 /* post state message to application */
1247 MMWFDSINK_POST_MESSAGE(wfd_sink,
1249 MMWFDSINK_CURRENT_STATE(wfd_sink));
1252 MMWFDSINK_PRINT_STATE(wfd_sink);
1254 wfd_sink_debug_fleave();
1256 return MM_ERROR_NONE;
1260 __mm_wfd_sink_set_pipeline_state(mm_wfd_sink_t *wfd_sink, GstState state, gboolean async)
1262 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
1263 GstState cur_state = GST_STATE_VOID_PENDING;
1264 GstState pending_state = GST_STATE_VOID_PENDING;
1266 wfd_sink_debug_fenter();
1268 wfd_sink_return_val_if_fail(wfd_sink &&
1269 wfd_sink->pipeline &&
1270 wfd_sink->pipeline->mainbin &&
1271 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst,
1272 MM_ERROR_WFD_NOT_INITIALIZED);
1274 wfd_sink_return_val_if_fail(state > GST_STATE_VOID_PENDING,
1275 MM_ERROR_WFD_INVALID_ARGUMENT);
1277 wfd_sink_debug("try to set %s state ", gst_element_state_get_name(state));
1279 result = gst_element_set_state(wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst, state);
1280 if (result == GST_STATE_CHANGE_FAILURE) {
1281 wfd_sink_error("fail to set %s state....", gst_element_state_get_name(state));
1282 return MM_ERROR_WFD_INTERNAL;
1286 wfd_sink_debug("wait for changing state is completed ");
1288 result = gst_element_get_state(wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst,
1289 &cur_state, &pending_state, wfd_sink->ini.state_change_timeout * GST_SECOND);
1290 if (result == GST_STATE_CHANGE_FAILURE) {
1291 wfd_sink_error("fail to get state within %d seconds....", wfd_sink->ini.state_change_timeout);
1293 __mm_wfd_sink_dump_pipeline_state(wfd_sink);
1295 return MM_ERROR_WFD_INTERNAL;
1296 } else if (result == GST_STATE_CHANGE_NO_PREROLL) {
1297 wfd_sink_debug("successfully changed state but is not able to provide data yet");
1300 wfd_sink_debug("cur state is %s, pending state is %s",
1301 gst_element_state_get_name(cur_state),
1302 gst_element_state_get_name(pending_state));
1306 wfd_sink_debug_fleave();
1308 return MM_ERROR_NONE;
1312 _mm_wfd_sink_reset_basetime(mm_wfd_sink_t *wfd_sink)
1314 GstClockTime base_time = GST_CLOCK_TIME_NONE;
1317 wfd_sink_debug_fenter();
1319 wfd_sink_return_if_fail(wfd_sink &&
1320 wfd_sink->pipeline &&
1321 wfd_sink->pipeline->mainbin &&
1322 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst);
1323 wfd_sink_return_if_fail(wfd_sink->need_to_reset_basetime);
1326 if (wfd_sink->clock)
1327 base_time = gst_clock_get_time(wfd_sink->clock);
1329 if (GST_CLOCK_TIME_IS_VALID(base_time)) {
1331 wfd_sink_debug("set pipeline base_time as now [%"GST_TIME_FORMAT"]", GST_TIME_ARGS(base_time));
1333 for (i = 0; i < WFD_SINK_M_NUM; i++) {
1334 if (wfd_sink->pipeline->mainbin[i].gst)
1335 gst_element_set_base_time(GST_ELEMENT_CAST(wfd_sink->pipeline->mainbin[i].gst), base_time);
1338 if (wfd_sink->pipeline->v_decodebin) {
1339 for (i = 0; i < WFD_SINK_V_D_NUM; i++) {
1340 if (wfd_sink->pipeline->v_decodebin[i].gst)
1341 gst_element_set_base_time(GST_ELEMENT_CAST(wfd_sink->pipeline->v_decodebin[i].gst), base_time);
1345 if (wfd_sink->pipeline->v_sinkbin) {
1346 for (i = 0; i < WFD_SINK_V_S_NUM; i++) {
1347 if (wfd_sink->pipeline->v_sinkbin[i].gst)
1348 gst_element_set_base_time(GST_ELEMENT_CAST(wfd_sink->pipeline->v_sinkbin[i].gst), base_time);
1352 if (wfd_sink->pipeline->a_decodebin) {
1353 for (i = 0; i < WFD_SINK_A_D_NUM; i++) {
1354 if (wfd_sink->pipeline->a_decodebin[i].gst)
1355 gst_element_set_base_time(GST_ELEMENT_CAST(wfd_sink->pipeline->a_decodebin[i].gst), base_time);
1359 if (wfd_sink->pipeline->a_sinkbin) {
1360 for (i = 0; i < WFD_SINK_A_S_NUM; i++) {
1361 if (wfd_sink->pipeline->a_sinkbin[i].gst)
1362 gst_element_set_base_time(GST_ELEMENT_CAST(wfd_sink->pipeline->a_sinkbin[i].gst), base_time);
1366 wfd_sink->need_to_reset_basetime = FALSE;
1369 wfd_sink_debug_fleave();
1375 __mm_wfd_sink_unprepare_video_pipeline(mm_wfd_sink_t *wfd_sink)
1377 GstElement *pipeline = NULL;
1378 GstElement *v_decodebin = NULL;
1379 GstElement *v_sinkbin = NULL;
1380 GstPad *sinkpad = NULL;
1381 GstPad *srcpad = NULL;
1382 int ret = MM_ERROR_NONE;
1384 wfd_sink_debug_fenter();
1386 wfd_sink_return_val_if_fail(wfd_sink &&
1388 MM_ERROR_WFD_NOT_INITIALIZED);
1390 if (wfd_sink->stream_info.video_stream_info.codec == MM_WFD_SINK_VIDEO_CODEC_NONE) {
1391 wfd_sink_debug("Skip unprepare video pipeline for none audio codec.");
1392 wfd_sink_debug_fleave();
1393 return MM_ERROR_NONE;
1396 PRINT_WFD_REF_COUNT(wfd_sink);
1397 wfd_sink_error("No-error:unprepare video sink bin");
1398 if (wfd_sink->pipeline->v_sinkbin && wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_BIN].gst) {
1399 v_sinkbin = wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_BIN].gst;
1401 if ((pipeline = GST_ELEMENT_CAST(gst_element_get_parent(v_sinkbin)))) {
1402 sinkpad = gst_element_get_static_pad(v_sinkbin, "sink");
1404 wfd_sink_error("failed to get sink pad from %s", GST_ELEMENT_NAME(v_sinkbin));
1408 if (gst_pad_is_linked(sinkpad)) {
1409 srcpad = gst_pad_get_peer(sinkpad);
1411 wfd_sink_error("failed to get peer pad of %s:%s",
1412 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1416 wfd_sink_error("try to unlink %s:%s and %s:%s",
1417 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1418 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1419 if (!gst_pad_unlink(srcpad, sinkpad)) {
1420 wfd_sink_error("failed to unlink %s:%s and %s:%s",
1421 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1422 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1425 gst_object_unref(srcpad);
1428 wfd_sink_debug("video sinkbin's sinkpad is not linked, no need to unlink it");
1430 gst_object_unref(sinkpad);
1433 wfd_sink_error("try to remove %s from %s",
1434 GST_ELEMENT_NAME(v_sinkbin), GST_ELEMENT_NAME(pipeline));
1436 gst_object_ref(v_sinkbin);
1437 if (!gst_bin_remove(GST_BIN(pipeline), GST_ELEMENT(v_sinkbin))) {
1438 wfd_sink_error("failed to remove %s from %s",
1439 GST_ELEMENT_NAME(v_sinkbin), GST_ELEMENT_NAME(pipeline));
1443 gst_object_unref(pipeline);
1447 ret = __mm_wfd_sink_destroy_video_sinkbin(wfd_sink);
1448 if (ret != MM_ERROR_NONE) {
1449 wfd_sink_error("failed to destroy video sinkbin");
1453 PRINT_WFD_REF_COUNT(wfd_sink);
1455 wfd_sink_error("No-error:unprepare video decode bin");
1456 if (wfd_sink->pipeline->v_decodebin && wfd_sink->pipeline->v_decodebin[WFD_SINK_V_D_BIN].gst) {
1457 v_decodebin = wfd_sink->pipeline->v_decodebin[WFD_SINK_V_D_BIN].gst;
1459 if ((pipeline = GST_ELEMENT_CAST(gst_element_get_parent(v_decodebin)))) {
1460 sinkpad = gst_element_get_static_pad(v_decodebin, "sink");
1462 wfd_sink_error("failed to get sink pad from %s", GST_ELEMENT_NAME(v_decodebin));
1466 if (gst_pad_is_linked(sinkpad)) {
1467 srcpad = gst_pad_get_peer(sinkpad);
1469 wfd_sink_error("failed to get peer pad of %s:%s",
1470 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1474 wfd_sink_error("try to unlink %s:%s and %s:%s",
1475 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1476 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1477 if (!gst_pad_unlink(srcpad, sinkpad)) {
1478 wfd_sink_error("failed 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));
1483 gst_object_unref(srcpad);
1486 wfd_sink_debug("video decodebin's sinkpad is not linked, no need to unlink it");
1488 gst_object_unref(sinkpad);
1491 srcpad = gst_element_get_static_pad(v_decodebin, "src");
1493 wfd_sink_error("failed to get src pad from %s", GST_ELEMENT_NAME(v_decodebin));
1497 if (gst_pad_is_linked(srcpad)) {
1498 sinkpad = gst_pad_get_peer(srcpad);
1500 wfd_sink_error("failed to get peer pad of %s:%s",
1501 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad));
1505 wfd_sink_error("try to unlink %s:%s and %s:%s",
1506 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1507 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1508 if (!gst_pad_unlink(srcpad, sinkpad)) {
1509 wfd_sink_error("failed 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));
1514 gst_object_unref(sinkpad);
1517 wfd_sink_debug("video decodebin's srcpad is not linked, no need to unlink it");
1519 gst_object_unref(srcpad);
1522 wfd_sink_error("try to remove %s from %s",
1523 GST_ELEMENT_NAME(v_decodebin), GST_ELEMENT_NAME(pipeline));
1524 gst_object_ref(v_decodebin);
1525 if (!gst_bin_remove(GST_BIN(pipeline), GST_ELEMENT(v_decodebin))) {
1526 wfd_sink_error("failed to remove %s from %s",
1527 GST_ELEMENT_NAME(v_decodebin), GST_ELEMENT_NAME(pipeline));
1531 gst_object_unref(pipeline);
1535 ret = __mm_wfd_sink_destroy_video_decodebin(wfd_sink);
1536 if (ret != MM_ERROR_NONE) {
1537 wfd_sink_error("failed to destroy video decodebin");
1541 PRINT_WFD_REF_COUNT(wfd_sink);
1543 wfd_sink_debug_fleave();
1550 gst_object_unref(pipeline);
1555 gst_object_unref(sinkpad);
1560 gst_object_unref(srcpad);
1564 /* need to notify to app */
1565 MMWFDSINK_POST_MESSAGE(wfd_sink,
1566 MM_ERROR_WFD_INTERNAL,
1567 MMWFDSINK_CURRENT_STATE(wfd_sink));
1569 return MM_ERROR_WFD_INTERNAL;
1573 __mm_wfd_sink_prepare_video_pipeline(mm_wfd_sink_t *wfd_sink, GstPad **pad)
1575 GstElement *pipeline = NULL;
1576 GstElement *v_decodebin = NULL;
1577 GstElement *v_sinkbin = NULL;
1578 GstPad *srcpad = NULL;
1579 GstPad *sinkpad = NULL;
1581 wfd_sink_debug_fenter();
1583 wfd_sink_return_val_if_fail(wfd_sink &&
1584 wfd_sink->pipeline &&
1585 wfd_sink->pipeline->mainbin &&
1586 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst,
1587 MM_ERROR_WFD_NOT_INITIALIZED);
1589 if (wfd_sink->stream_info.video_stream_info.codec == MM_WFD_SINK_VIDEO_CODEC_NONE) {
1590 wfd_sink_debug("Skip prepare video pipeline for none audio codec");
1591 wfd_sink_debug_fleave();
1592 return MM_ERROR_NONE;
1595 /* check video decodebin is linked */
1596 if (!wfd_sink->video_decodebin_is_linked) {
1597 /* check video decodebin is created */
1598 if (wfd_sink->pipeline->v_decodebin == NULL) {
1599 if (MM_ERROR_NONE != __mm_wfd_sink_create_video_decodebin(wfd_sink)) {
1600 wfd_sink_error("failed to create video decodebin....");
1605 if (MM_ERROR_NONE != __mm_wfd_sink_link_video_decodebin(wfd_sink)) {
1606 wfd_sink_error("failed to link video decodebin.....");
1611 /* check video sinkbin is created */
1612 if (wfd_sink->pipeline->v_sinkbin == NULL) {
1613 if (MM_ERROR_NONE != __mm_wfd_sink_create_video_sinkbin(wfd_sink)) {
1614 wfd_sink_error("failed to create video sinkbin....");
1618 /* add video decodebin to pipeline */
1619 if (wfd_sink->pipeline->v_decodebin && wfd_sink->pipeline->v_decodebin[WFD_SINK_V_D_BIN].gst) {
1620 v_decodebin = wfd_sink->pipeline->v_decodebin[WFD_SINK_V_D_BIN].gst;
1622 pipeline = GST_ELEMENT_CAST(gst_element_get_parent(v_decodebin));
1624 pipeline = wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst;
1626 if (GST_STATE(v_decodebin) <= GST_STATE_NULL) {
1627 wfd_sink_debug("need to prepare %s", GST_ELEMENT_NAME(v_decodebin));
1628 if (GST_STATE_CHANGE_FAILURE == gst_element_set_state(v_decodebin, GST_STATE_READY)) {
1629 wfd_sink_error("failed to set state(READY) to video decodebin");
1634 wfd_sink_debug("try to add %s to %s",
1635 GST_ELEMENT_NAME(v_decodebin), GST_ELEMENT_NAME(pipeline));
1636 if (!gst_bin_add(GST_BIN(pipeline), GST_ELEMENT(v_decodebin))) {
1637 wfd_sink_error("failed to add %s to %s",
1638 GST_ELEMENT_NAME(v_decodebin), GST_ELEMENT_NAME(pipeline));
1642 wfd_sink_debug("need to sync state %s with its parent", GST_ELEMENT_NAME(v_decodebin));
1643 if (!gst_element_sync_state_with_parent(GST_ELEMENT(v_decodebin))) {
1644 wfd_sink_error("failed to sync %s state with parent", GST_ELEMENT_NAME(v_decodebin));
1648 wfd_sink_debug("%s is already added to %s",
1649 GST_ELEMENT_NAME(v_decodebin), GST_ELEMENT_NAME(pipeline));
1650 gst_object_unref(pipeline);
1654 wfd_sink_warning("going on without video decodebin....");
1657 /* add video sinkbin to pipeline */
1658 if (wfd_sink->pipeline->v_sinkbin && wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_BIN].gst) {
1659 v_sinkbin = wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_BIN].gst;
1661 pipeline = GST_ELEMENT_CAST(gst_element_get_parent(v_sinkbin));
1663 pipeline = wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst;
1665 /* prepare video sinkbin before adding */
1666 if (GST_STATE(v_sinkbin) <= GST_STATE_NULL) {
1667 wfd_sink_debug("need to prepare %s", GST_ELEMENT_NAME(v_sinkbin));
1668 if (GST_STATE_CHANGE_FAILURE == gst_element_set_state(v_sinkbin, GST_STATE_READY)) {
1669 wfd_sink_error("failed to set state(READY) to video sinkbin");
1673 /* add video sinkbin to pipeline */
1674 wfd_sink_debug("try to add %s to %s",
1675 GST_ELEMENT_NAME(v_sinkbin), GST_ELEMENT_NAME(pipeline));
1676 if (!gst_bin_add(GST_BIN(pipeline), GST_ELEMENT(v_sinkbin))) {
1677 wfd_sink_error("failed to add %s to %s",
1678 GST_ELEMENT_NAME(v_sinkbin), GST_ELEMENT_NAME(pipeline));
1682 /* sync state with parent */
1683 wfd_sink_debug("need to sync state %s with its parent", GST_ELEMENT_NAME(v_sinkbin));
1684 if (!gst_element_sync_state_with_parent(GST_ELEMENT(v_sinkbin))) {
1685 wfd_sink_error("failed to sync %s state with parent", GST_ELEMENT_NAME(v_sinkbin));
1689 wfd_sink_debug("%s is already added to %s",
1690 GST_ELEMENT_NAME(v_sinkbin), GST_ELEMENT_NAME(pipeline));
1691 gst_object_unref(pipeline);
1695 wfd_sink_warning("going on without video sinkbin....");
1699 /* link video decodebin and sinkbin */
1700 if (wfd_sink->pipeline->v_decodebin && wfd_sink->pipeline->v_decodebin[WFD_SINK_V_D_BIN].gst) {
1701 v_decodebin = wfd_sink->pipeline->v_decodebin[WFD_SINK_V_D_BIN].gst;
1704 *pad = gst_element_get_static_pad(v_decodebin, "sink");
1706 if (wfd_sink->pipeline->v_sinkbin && wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_BIN].gst) {
1708 v_sinkbin = wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_BIN].gst;
1710 srcpad = gst_element_get_static_pad(v_decodebin, "src");
1712 wfd_sink_error("faied to get srcpad from %s", GST_ELEMENT_NAME(v_decodebin));
1716 if (!gst_pad_is_linked(srcpad)) {
1717 sinkpad = gst_element_get_static_pad(v_sinkbin, "sink");
1719 wfd_sink_error("faied to get sinkpad from %s", GST_ELEMENT_NAME(v_sinkbin));
1723 wfd_sink_debug("try to link %s:%s and %s:%s",
1724 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1725 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1726 if (GST_PAD_LINK_OK != gst_pad_link_full(srcpad, sinkpad, GST_PAD_LINK_CHECK_NOTHING)) {
1727 wfd_sink_error("failed to link %s:%s and %s:%s",
1728 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1729 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1732 gst_object_unref(sinkpad);
1735 gst_object_unref(srcpad);
1738 } else if (wfd_sink->pipeline->v_sinkbin && wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_BIN].gst) {
1739 v_sinkbin = wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_BIN].gst;
1741 *pad = gst_element_get_static_pad(v_sinkbin, "sink");
1744 wfd_sink_debug_fleave();
1746 return MM_ERROR_NONE;
1750 if (sinkpad != NULL) {
1751 gst_object_unref(sinkpad);
1755 if (srcpad != NULL) {
1756 gst_object_unref(srcpad);
1760 /* need to notify to app */
1761 MMWFDSINK_POST_MESSAGE(wfd_sink,
1762 MM_ERROR_WFD_INTERNAL,
1763 MMWFDSINK_CURRENT_STATE(wfd_sink));
1765 return MM_ERROR_WFD_INTERNAL;
1769 __mm_wfd_sink_unprepare_audio_pipeline(mm_wfd_sink_t *wfd_sink)
1771 GstElement *pipeline = NULL;
1772 GstElement *a_decodebin = NULL;
1773 GstElement *a_sinkbin = NULL;
1774 GstPad *sinkpad = NULL;
1775 GstPad *srcpad = NULL;
1776 int ret = MM_ERROR_NONE;
1778 wfd_sink_debug_fenter();
1780 wfd_sink_return_val_if_fail(wfd_sink &&
1782 MM_ERROR_WFD_NOT_INITIALIZED);
1784 if (wfd_sink->stream_info.audio_stream_info.codec == MM_WFD_SINK_AUDIO_CODEC_NONE) {
1785 wfd_sink_debug("Skip unprepare audio pipeline for none audio codec.");
1786 wfd_sink_debug_fleave();
1787 return MM_ERROR_NONE;
1790 wfd_sink_error("No-error:unprepare audio sink bin");
1791 PRINT_WFD_REF_COUNT(wfd_sink);
1793 if (wfd_sink->pipeline->a_sinkbin && wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_BIN].gst) {
1794 a_sinkbin = wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_BIN].gst;
1796 if ((pipeline = GST_ELEMENT_CAST(gst_element_get_parent(a_sinkbin)))) {
1797 sinkpad = gst_element_get_static_pad(a_sinkbin, "sink");
1799 wfd_sink_error("failed to get sink pad from %s", GST_ELEMENT_NAME(a_sinkbin));
1803 if (gst_pad_is_linked(sinkpad)) {
1804 srcpad = gst_pad_get_peer(sinkpad);
1806 wfd_sink_error("failed to get peer pad of %s:%s",
1807 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1811 wfd_sink_error("try to unlink %s:%s and %s:%s",
1812 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1813 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1814 if (!gst_pad_unlink(srcpad, sinkpad)) {
1815 wfd_sink_error("failed to unlink %s:%s and %s:%s",
1816 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1817 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1820 gst_object_unref(srcpad);
1823 wfd_sink_debug("audio sinkbin's sinkpad is not linked, no need to unlink it");
1825 gst_object_unref(sinkpad);
1828 wfd_sink_error("try to remove %s from %s", GST_ELEMENT_NAME(a_sinkbin), GST_ELEMENT_NAME(pipeline));
1830 gst_object_ref(a_sinkbin);
1831 if (!gst_bin_remove(GST_BIN(pipeline), GST_ELEMENT(a_sinkbin))) {
1832 wfd_sink_error("failed to remove %s from %s",
1833 GST_ELEMENT_NAME(a_sinkbin), GST_ELEMENT_NAME(pipeline));
1837 gst_object_unref(pipeline);
1841 ret = __mm_wfd_sink_destroy_audio_sinkbin(wfd_sink);
1842 if (ret != MM_ERROR_NONE) {
1843 wfd_sink_error("failed to destroy audio sinkbin");
1847 PRINT_WFD_REF_COUNT(wfd_sink);
1849 wfd_sink_error("No-error:unprepare audio decode bin");
1850 if (wfd_sink->pipeline->a_decodebin && wfd_sink->pipeline->a_decodebin[WFD_SINK_A_D_BIN].gst) {
1851 a_decodebin = wfd_sink->pipeline->a_decodebin[WFD_SINK_A_D_BIN].gst;
1853 if ((pipeline = GST_ELEMENT_CAST(gst_element_get_parent(a_decodebin)))) {
1854 sinkpad = gst_element_get_static_pad(a_decodebin, "sink");
1856 wfd_sink_error("failed to get sink pad from %s", GST_ELEMENT_NAME(a_decodebin));
1860 if (gst_pad_is_linked(sinkpad)) {
1861 srcpad = gst_pad_get_peer(sinkpad);
1863 wfd_sink_error("failed to get peer pad of %s:%s",
1864 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1868 wfd_sink_error("try 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));
1871 if (!gst_pad_unlink(srcpad, sinkpad)) {
1872 wfd_sink_error("failed to unlink %s:%s and %s:%s",
1873 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1874 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1877 gst_object_unref(srcpad);
1880 wfd_sink_debug("audio decodebin's sinkpad is not linked, no need to unlink it");
1882 gst_object_unref(sinkpad);
1885 srcpad = gst_element_get_static_pad(a_decodebin, "src");
1887 wfd_sink_error("failed to get src pad from %s", GST_ELEMENT_NAME(a_decodebin));
1891 if (gst_pad_is_linked(srcpad)) {
1892 sinkpad = gst_pad_get_peer(srcpad);
1894 wfd_sink_error("failed to get peer pad of %s:%s",
1895 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad));
1896 return MM_ERROR_WFD_INTERNAL;
1899 wfd_sink_error("try 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 if (!gst_pad_unlink(srcpad, sinkpad)) {
1903 wfd_sink_error("failed to unlink %s:%s and %s:%s",
1904 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
1905 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
1906 return MM_ERROR_WFD_INTERNAL;
1908 gst_object_unref(sinkpad);
1911 wfd_sink_debug("audio decodebin's srcpad is not linked, no need to unlink it");
1913 gst_object_unref(srcpad);
1916 wfd_sink_error("try to remove %s from %s",
1917 GST_ELEMENT_NAME(a_decodebin), GST_ELEMENT_NAME(pipeline));
1918 gst_object_ref(a_decodebin);
1919 if (!gst_bin_remove(GST_BIN(pipeline), GST_ELEMENT(a_decodebin))) {
1920 wfd_sink_error("failed to remove %s from %s",
1921 GST_ELEMENT_NAME(a_decodebin), GST_ELEMENT_NAME(pipeline));
1925 gst_object_unref(pipeline);
1929 ret = __mm_wfd_sink_destroy_audio_decodebin(wfd_sink);
1930 if (ret != MM_ERROR_NONE) {
1931 wfd_sink_error("failed to destroy audio decodebin");
1935 PRINT_WFD_REF_COUNT(wfd_sink);
1937 wfd_sink_debug_fleave();
1944 gst_object_unref(pipeline);
1948 gst_object_unref(sinkpad);
1953 gst_object_unref(srcpad);
1957 /* need to notify to app */
1958 MMWFDSINK_POST_MESSAGE(wfd_sink,
1959 MM_ERROR_WFD_INTERNAL,
1960 MMWFDSINK_CURRENT_STATE(wfd_sink));
1962 return MM_ERROR_WFD_INTERNAL;
1966 __mm_wfd_sink_prepare_audio_pipeline(mm_wfd_sink_t *wfd_sink, GstPad **pad)
1968 GstElement *pipeline = NULL;
1969 GstElement *a_decodebin = NULL;
1970 GstElement *a_sinkbin = NULL;
1971 GstPad *srcpad = NULL;
1972 GstPad *sinkpad = NULL;
1974 wfd_sink_debug_fenter();
1976 wfd_sink_return_val_if_fail(wfd_sink &&
1977 wfd_sink->pipeline &&
1978 wfd_sink->pipeline->mainbin &&
1979 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst,
1980 MM_ERROR_WFD_NOT_INITIALIZED);
1982 if (wfd_sink->stream_info.audio_stream_info.codec == MM_WFD_SINK_AUDIO_CODEC_NONE) {
1983 wfd_sink_debug("Skip prepare audio pipeline for none audio codec");
1984 wfd_sink_debug_fleave();
1985 return MM_ERROR_NONE;
1988 /* check audio decodebin is linked */
1989 if (!wfd_sink->audio_decodebin_is_linked) {
1990 /* check audio decodebin is created */
1991 if (wfd_sink->pipeline->a_decodebin == NULL) {
1992 if (MM_ERROR_NONE != __mm_wfd_sink_create_audio_decodebin(wfd_sink)) {
1993 wfd_sink_error("failed to create audio decodebin....");
1998 if (MM_ERROR_NONE != __mm_wfd_sink_link_audio_decodebin(wfd_sink)) {
1999 wfd_sink_error("failed to link audio decodebin.....");
2004 /* check audio sinkbin is created */
2005 if (wfd_sink->pipeline->a_sinkbin == NULL) {
2006 if (MM_ERROR_NONE != __mm_wfd_sink_create_audio_sinkbin(wfd_sink)) {
2007 wfd_sink_error("failed to create audio sinkbin....");
2012 /* add audio decodebin to pipeline */
2013 if (wfd_sink->pipeline->a_decodebin && wfd_sink->pipeline->a_decodebin[WFD_SINK_A_D_BIN].gst) {
2014 a_decodebin = wfd_sink->pipeline->a_decodebin[WFD_SINK_A_D_BIN].gst;
2016 pipeline = GST_ELEMENT_CAST(gst_element_get_parent(a_decodebin));
2018 pipeline = wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst;
2020 if (GST_STATE(a_decodebin) <= GST_STATE_NULL) {
2021 wfd_sink_debug("need to prepare %s", GST_ELEMENT_NAME(a_decodebin));
2022 if (GST_STATE_CHANGE_FAILURE == gst_element_set_state(a_decodebin, GST_STATE_READY)) {
2023 wfd_sink_error("failed to set state(READY) to audio decodebin");
2028 wfd_sink_debug("try to add %s to %s",
2029 GST_ELEMENT_NAME(a_decodebin), GST_ELEMENT_NAME(pipeline));
2030 if (!gst_bin_add(GST_BIN(pipeline), GST_ELEMENT(a_decodebin))) {
2031 wfd_sink_error("failed to add %s to %s",
2032 GST_ELEMENT_NAME(a_decodebin), GST_ELEMENT_NAME(pipeline));
2036 wfd_sink_debug("need to sync state %s with its parent", GST_ELEMENT_NAME(a_decodebin));
2037 if (!gst_element_sync_state_with_parent(GST_ELEMENT(a_decodebin))) {
2038 wfd_sink_error("failed to sync %s state with parent", GST_ELEMENT_NAME(a_decodebin));
2042 wfd_sink_debug("%s is already added to %s",
2043 GST_ELEMENT_NAME(a_decodebin), GST_ELEMENT_NAME(pipeline));
2044 gst_object_unref(pipeline);
2048 wfd_sink_warning("going on without audio decodebin....");
2051 /* add audio sinkbin to pipeline */
2052 if (wfd_sink->pipeline->a_sinkbin && wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_BIN].gst) {
2053 a_sinkbin = wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_BIN].gst;
2055 pipeline = GST_ELEMENT_CAST(gst_element_get_parent(a_sinkbin));
2057 pipeline = wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst;
2059 /* prepare audio sinkbin before adding */
2060 if (GST_STATE(a_sinkbin) <= GST_STATE_NULL) {
2061 wfd_sink_debug("need to prepare %s", GST_ELEMENT_NAME(a_sinkbin));
2062 if (GST_STATE_CHANGE_FAILURE == gst_element_set_state(a_sinkbin, GST_STATE_READY)) {
2063 wfd_sink_error("failed to set state(READY) to audio sinkbin");
2068 /* add audio sinkbin to pipeline */
2069 wfd_sink_debug("try to add %s to %s",
2070 GST_ELEMENT_NAME(a_sinkbin), GST_ELEMENT_NAME(pipeline));
2071 if (!gst_bin_add(GST_BIN(pipeline), GST_ELEMENT(a_sinkbin))) {
2072 wfd_sink_error("failed to add %s to %s",
2073 GST_ELEMENT_NAME(a_sinkbin), GST_ELEMENT_NAME(pipeline));
2077 /* sync state with parent */
2078 wfd_sink_debug("need to sync state %s with its parent", GST_ELEMENT_NAME(a_sinkbin));
2079 if (!gst_element_sync_state_with_parent(GST_ELEMENT(a_sinkbin))) {
2080 wfd_sink_error("failed to sync %s state with parent", GST_ELEMENT_NAME(a_sinkbin));
2084 wfd_sink_debug("%s is already added to %s",
2085 GST_ELEMENT_NAME(a_sinkbin), GST_ELEMENT_NAME(pipeline));
2086 gst_object_unref(pipeline);
2090 wfd_sink_warning("going on without audio sinkbin....");
2094 /* link audio decodebin and sinkbin */
2095 if (wfd_sink->pipeline->a_decodebin && wfd_sink->pipeline->a_decodebin[WFD_SINK_A_D_BIN].gst) {
2096 a_decodebin = wfd_sink->pipeline->a_decodebin[WFD_SINK_A_D_BIN].gst;
2099 *pad = gst_element_get_static_pad(a_decodebin, "sink");
2101 if (wfd_sink->pipeline->a_sinkbin && wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_BIN].gst) {
2103 a_sinkbin = wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_BIN].gst;
2105 srcpad = gst_element_get_static_pad(a_decodebin, "src");
2107 wfd_sink_error("faied to get srcpad from %s", GST_ELEMENT_NAME(a_decodebin));
2111 if (!gst_pad_is_linked(srcpad)) {
2112 sinkpad = gst_element_get_static_pad(a_sinkbin, "sink");
2114 wfd_sink_error("faied to get sinkpad from %s", GST_ELEMENT_NAME(a_sinkbin));
2118 wfd_sink_debug("try 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));
2121 if (GST_PAD_LINK_OK != gst_pad_link_full(srcpad, sinkpad, GST_PAD_LINK_CHECK_NOTHING)) {
2122 wfd_sink_error("failed to link %s:%s and %s:%s",
2123 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
2124 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
2128 gst_object_unref(sinkpad);
2131 gst_object_unref(srcpad);
2134 } else if (wfd_sink->pipeline->a_sinkbin && wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_BIN].gst) {
2135 a_sinkbin = wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_BIN].gst;
2137 *pad = gst_element_get_static_pad(a_sinkbin, "sink");
2140 wfd_sink_debug_fleave();
2142 return MM_ERROR_NONE;
2147 gst_object_unref(pipeline);
2152 gst_object_unref(sinkpad);
2157 gst_object_unref(srcpad);
2161 /* need to notify to app */
2162 MMWFDSINK_POST_MESSAGE(wfd_sink,
2163 MM_ERROR_WFD_INTERNAL,
2164 MMWFDSINK_CURRENT_STATE(wfd_sink));
2166 return MM_ERROR_WFD_INTERNAL;
2169 #define COMPENSATION_CRETERIA_VALUE 1000000 /* 1 msec */
2170 #define COMPENSATION_CHECK_PERIOD (30*GST_SECOND) /* 30 sec */
2172 static GstPadProbeReturn
2173 _mm_wfd_sink_check_running_time(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
2175 mm_wfd_sink_t *wfd_sink = (mm_wfd_sink_t *)u_data;
2176 GstClockTime current_time = GST_CLOCK_TIME_NONE;
2177 GstClockTime start_time = GST_CLOCK_TIME_NONE;
2178 GstClockTime running_time = GST_CLOCK_TIME_NONE;
2179 GstClockTime base_time = GST_CLOCK_TIME_NONE;
2180 GstClockTime render_time = GST_CLOCK_TIME_NONE;
2181 GstClockTimeDiff diff = GST_CLOCK_TIME_NONE;
2182 GstBuffer *buffer = NULL;
2183 gint64 ts_offset = 0LL;
2185 wfd_sink_return_val_if_fail(info, FALSE);
2186 wfd_sink_return_val_if_fail(wfd_sink &&
2187 wfd_sink->pipeline &&
2188 wfd_sink->pipeline->mainbin &&
2189 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst,
2190 GST_PAD_PROBE_DROP);
2192 if (!wfd_sink->clock) {
2193 wfd_sink_warning("pipeline did not select clock, yet");
2194 return GST_PAD_PROBE_OK;
2197 if (wfd_sink->need_to_reset_basetime)
2198 _mm_wfd_sink_reset_basetime(wfd_sink);
2200 /* calculate current runninig time */
2201 current_time = gst_clock_get_time(wfd_sink->clock);
2202 if (g_strrstr(GST_OBJECT_NAME(pad), "video"))
2203 base_time = gst_element_get_base_time(wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_BIN].gst);
2204 else if (g_strrstr(GST_OBJECT_NAME(pad), "audio"))
2205 base_time = gst_element_get_base_time(wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_BIN].gst);
2206 start_time = gst_element_get_start_time(wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst);
2207 if (GST_CLOCK_TIME_IS_VALID(current_time) &&
2208 GST_CLOCK_TIME_IS_VALID(start_time) &&
2209 GST_CLOCK_TIME_IS_VALID(base_time)) {
2210 running_time = current_time - (start_time + base_time);
2212 wfd_sink_debug("current time %"GST_TIME_FORMAT", start time %"GST_TIME_FORMAT
2213 " base time %"GST_TIME_FORMAT"", GST_TIME_ARGS(current_time),
2214 GST_TIME_ARGS(start_time), GST_TIME_ARGS(base_time));
2215 return GST_PAD_PROBE_OK;
2218 /* calculate this buffer rendering time */
2219 buffer = gst_pad_probe_info_get_buffer(info);
2220 if (!GST_BUFFER_TIMESTAMP_IS_VALID(buffer)) {
2221 wfd_sink_warning("buffer timestamp is invalid.");
2222 return GST_PAD_PROBE_OK;
2225 if (g_strrstr(GST_OBJECT_NAME(pad), "audio")) {
2226 if (wfd_sink->pipeline && wfd_sink->pipeline->a_sinkbin && wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_SINK].gst)
2227 g_object_get(G_OBJECT(wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_SINK].gst), "ts-offset", &ts_offset, NULL);
2228 } else if (g_strrstr(GST_OBJECT_NAME(pad), "video")) {
2229 if (wfd_sink->pipeline && wfd_sink->pipeline->v_sinkbin && wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_SINK].gst)
2230 g_object_get(G_OBJECT(wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_SINK].gst), "ts-offset", &ts_offset, NULL);
2233 render_time = GST_BUFFER_TIMESTAMP(buffer);
2234 render_time += ts_offset;
2236 /* chekc this buffer could be rendered or not */
2237 if (GST_CLOCK_TIME_IS_VALID(running_time) && GST_CLOCK_TIME_IS_VALID(render_time)) {
2238 diff = GST_CLOCK_DIFF(running_time, render_time);
2240 /* this buffer could be NOT rendered */
2241 wfd_sink_debug("%s : diff time : -%" GST_TIME_FORMAT "",
2242 GST_STR_NULL((GST_OBJECT_NAME(pad))),
2243 GST_TIME_ARGS(GST_CLOCK_DIFF(render_time, running_time)));
2245 /* this buffer could be rendered */
2246 /*wfd_sink_debug ("%s :diff time : %" GST_TIME_FORMAT "", */
2247 /* GST_STR_NULL((GST_OBJECT_NAME(pad))), */
2248 /* GST_TIME_ARGS(diff)); */
2252 /* update buffer count and gap */
2253 if (g_strrstr(GST_OBJECT_NAME(pad), "video")) {
2254 wfd_sink->video_buffer_count++;
2255 wfd_sink->video_accumulated_gap += diff;
2256 } else if (g_strrstr(GST_OBJECT_NAME(pad), "audio")) {
2257 wfd_sink->audio_buffer_count++;
2258 wfd_sink->audio_accumulated_gap += diff;
2260 wfd_sink_warning("invalid buffer type.. ");
2261 return GST_PAD_PROBE_DROP;
2264 if (GST_CLOCK_TIME_IS_VALID(wfd_sink->last_buffer_timestamp)) {
2265 /* fisrt 60sec, just calculate the gap between source device and sink device */
2266 if (GST_BUFFER_TIMESTAMP(buffer) < 60 * GST_SECOND)
2267 return GST_PAD_PROBE_OK;
2269 /* every 10sec, calculate the gap between source device and sink device */
2270 if (GST_CLOCK_DIFF(wfd_sink->last_buffer_timestamp, GST_BUFFER_TIMESTAMP(buffer))
2271 > COMPENSATION_CHECK_PERIOD) {
2272 gint64 audio_avgrage_gap = 0LL;
2273 gint64 video_avgrage_gap = 0LL;
2274 gint64 audio_avgrage_gap_diff = 0LL;
2275 gint64 video_avgrage_gap_diff = 0LL;
2276 gboolean video_minus_compensation = FALSE;
2277 gboolean audio_minus_compensation = FALSE;
2278 gint64 avgrage_gap_diff = 0LL;
2279 gboolean minus_compensation = FALSE;
2282 if (wfd_sink->video_buffer_count > 0) {
2283 video_avgrage_gap = wfd_sink->video_accumulated_gap / wfd_sink->video_buffer_count;
2285 if (wfd_sink->video_average_gap != 0) {
2286 if (video_avgrage_gap > wfd_sink->video_average_gap) {
2287 video_avgrage_gap_diff = video_avgrage_gap - wfd_sink->video_average_gap;
2288 video_minus_compensation = TRUE;
2290 video_avgrage_gap_diff = wfd_sink->video_average_gap - video_avgrage_gap;
2291 video_minus_compensation = FALSE;
2294 wfd_sink_debug("first update video average gap(%"G_GINT64_FORMAT"d) ", video_avgrage_gap);
2295 wfd_sink->video_average_gap = video_avgrage_gap;
2298 wfd_sink_debug("there is no video buffer flow during %"GST_TIME_FORMAT
2299 " ~ %" GST_TIME_FORMAT"",
2300 GST_TIME_ARGS(wfd_sink->last_buffer_timestamp),
2301 GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(buffer)));
2305 if (wfd_sink->audio_buffer_count > 0) {
2306 audio_avgrage_gap = wfd_sink->audio_accumulated_gap / wfd_sink->audio_buffer_count;
2308 if (wfd_sink->audio_average_gap != 0) {
2309 if (audio_avgrage_gap > wfd_sink->audio_average_gap) {
2310 audio_avgrage_gap_diff = audio_avgrage_gap - wfd_sink->audio_average_gap;
2311 audio_minus_compensation = TRUE;
2313 audio_avgrage_gap_diff = wfd_sink->audio_average_gap - audio_avgrage_gap;
2314 audio_minus_compensation = FALSE;
2317 wfd_sink_debug("first update audio average gap(%"G_GINT64_FORMAT"d) ", audio_avgrage_gap);
2318 wfd_sink->audio_average_gap = audio_avgrage_gap;
2321 wfd_sink_debug("there is no audio buffer flow during %"GST_TIME_FORMAT
2322 " ~ %" GST_TIME_FORMAT"",
2323 GST_TIME_ARGS(wfd_sink->last_buffer_timestamp),
2324 GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(buffer)));
2327 /* selecet average_gap_diff between video and audio */
2328 /* which makes no buffer drop in the sink elements */
2329 if (video_avgrage_gap_diff && audio_avgrage_gap_diff) {
2330 if (!video_minus_compensation && !audio_minus_compensation) {
2331 minus_compensation = FALSE;
2332 if (video_avgrage_gap_diff > audio_avgrage_gap_diff)
2333 avgrage_gap_diff = video_avgrage_gap_diff;
2335 avgrage_gap_diff = audio_avgrage_gap_diff;
2336 } else if (video_minus_compensation && audio_minus_compensation) {
2337 minus_compensation = TRUE;
2338 if (video_avgrage_gap_diff > audio_avgrage_gap_diff)
2339 avgrage_gap_diff = audio_avgrage_gap_diff;
2341 avgrage_gap_diff = video_avgrage_gap_diff;
2343 minus_compensation = FALSE;
2344 if (!video_minus_compensation)
2345 avgrage_gap_diff = video_avgrage_gap_diff;
2347 avgrage_gap_diff = audio_avgrage_gap_diff;
2349 } else if (video_avgrage_gap_diff) {
2350 minus_compensation = video_minus_compensation;
2351 avgrage_gap_diff = video_avgrage_gap_diff;
2352 } else if (audio_avgrage_gap_diff) {
2353 minus_compensation = audio_minus_compensation;
2354 avgrage_gap_diff = audio_avgrage_gap_diff;
2357 wfd_sink_debug("average diff gap difference beween audio:%s%"G_GINT64_FORMAT"d and video:%s%"G_GINT64_FORMAT"d ",
2358 audio_minus_compensation ? "-" : "", audio_avgrage_gap_diff,
2359 video_minus_compensation ? "-" : "", video_avgrage_gap_diff);
2362 /* if calculated gap diff is larger than 1ms. need to compensate buffer timestamp */
2363 if (avgrage_gap_diff >= COMPENSATION_CRETERIA_VALUE) {
2364 if (minus_compensation)
2365 ts_offset -= avgrage_gap_diff;
2367 ts_offset += avgrage_gap_diff;
2369 wfd_sink_debug("do timestamp compensation : %s%"G_GINT64_FORMAT"d (ts-offset : %"
2370 GST_TIME_FORMAT") at(%" GST_TIME_FORMAT")",
2371 minus_compensation ? "-" : "", avgrage_gap_diff,
2372 GST_TIME_ARGS(ts_offset), GST_TIME_ARGS(running_time));
2374 if (wfd_sink->pipeline && wfd_sink->pipeline->a_sinkbin && wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_SINK].gst)
2375 g_object_set(G_OBJECT(wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_SINK].gst), "ts-offset", (gint64)ts_offset, NULL);
2376 if (wfd_sink->pipeline && wfd_sink->pipeline->v_sinkbin && wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_SINK].gst)
2377 g_object_set(G_OBJECT(wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_SINK].gst), "ts-offset", (gint64)ts_offset, NULL);
2379 wfd_sink_debug("don't need to do timestamp compensation : %s%"G_GINT64_FORMAT"d (ts-offset : %"GST_TIME_FORMAT ")",
2380 minus_compensation ? "-" : "", avgrage_gap_diff, GST_TIME_ARGS(ts_offset));
2384 wfd_sink->video_buffer_count = 0;
2385 wfd_sink->video_accumulated_gap = 0LL;
2386 wfd_sink->audio_buffer_count = 0;
2387 wfd_sink->audio_accumulated_gap = 0LL;
2388 wfd_sink->last_buffer_timestamp = GST_BUFFER_TIMESTAMP(buffer);
2391 wfd_sink_debug("first update last buffer timestamp :%" GST_TIME_FORMAT,
2392 GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(buffer)));
2393 wfd_sink->last_buffer_timestamp = GST_BUFFER_TIMESTAMP(buffer);
2396 return GST_PAD_PROBE_OK;
2401 __mm_wfd_sink_demux_pad_added(GstElement *demux, GstPad *pad, gpointer data)
2403 mm_wfd_sink_t *wfd_sink = (mm_wfd_sink_t *)data;
2405 GstElement *pipeline = NULL;
2406 GstElement *valve = NULL;
2407 GstPad *sinkpad = NULL;
2408 GstPad *srcpad = NULL;
2410 wfd_sink_debug_fenter();
2412 wfd_sink_return_if_fail(wfd_sink &&
2413 wfd_sink->pipeline &&
2414 wfd_sink->pipeline->mainbin &&
2415 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst);
2417 name = gst_pad_get_name(pad);
2419 wfd_sink_error("fail to get pad");
2423 wfd_sink_debug("Mux pad added, video_codec=%d, audio_codec=%d, name[0] = %c",
2424 wfd_sink->stream_info.video_stream_info.codec,
2425 wfd_sink->stream_info.audio_stream_info.codec,
2428 //In case of none vieo codec, we don't add video pad
2429 if (wfd_sink->stream_info.video_stream_info.codec == MM_WFD_SINK_VIDEO_CODEC_NONE && name[0] == 'v') {
2430 wfd_sink_error("Skip video pad add for none video codec");
2435 //In case of none audio codec, we don't add audio pad
2436 if (wfd_sink->stream_info.audio_stream_info.codec == MM_WFD_SINK_AUDIO_CODEC_NONE && name[0] == 'a') {
2437 wfd_sink_error("Skip audio pad add for none audio codec");
2442 pipeline = wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst;
2444 /* take srcpad from demuxer added pad */
2445 srcpad = gst_object_ref(pad);
2447 if (name[0] == 'v') {
2448 if (wfd_sink->pipeline->mainbin[WFD_SINK_M_V_VALVE].gst)
2449 valve = wfd_sink->pipeline->mainbin[WFD_SINK_M_V_VALVE].gst;
2450 } else if (name[0] == 'a') {
2451 if (wfd_sink->pipeline->mainbin[WFD_SINK_M_A_VALVE].gst)
2452 valve = wfd_sink->pipeline->mainbin[WFD_SINK_M_A_VALVE].gst;
2455 /* add, link and run the valve */
2457 wfd_sink_debug("try to add %s to pipeline", GST_ELEMENT_NAME(valve));
2459 if (!gst_bin_add(GST_BIN(pipeline), valve)) {
2460 wfd_sink_error("failed to add %s to pipeline",
2461 GST_ELEMENT_NAME(valve));
2465 sinkpad = gst_element_get_static_pad(valve, "sink");
2467 wfd_sink_error("failed to get sink pad from %s",
2468 GST_ELEMENT_NAME(valve));
2472 wfd_sink_debug("try to link %s:%s and %s:%s",
2473 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
2474 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
2475 if (GST_PAD_LINK_OK != gst_pad_link_full(srcpad, sinkpad, GST_PAD_LINK_CHECK_NOTHING)) {
2476 wfd_sink_error("failed to link %s:%s and %s:%s",
2477 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
2478 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
2481 gst_object_unref(GST_OBJECT(srcpad));
2483 gst_object_unref(GST_OBJECT(sinkpad));
2486 wfd_sink_debug("try to sync %s's state with parent", GST_ELEMENT_NAME(valve));
2487 if (!gst_element_sync_state_with_parent(GST_ELEMENT_CAST(valve))) {
2488 wfd_sink_error("failed to sync %s state with parent",
2489 GST_PAD_NAME(valve));
2493 srcpad = gst_element_get_static_pad(valve, "src");
2495 wfd_sink_error("failed to get src pad from %s",
2496 GST_ELEMENT_NAME(valve));
2501 /* take decodebin/sinkbin */
2502 if (name[0] == 'v') {
2503 wfd_sink_debug("=========== >>>>>>>>>> Received VIDEO pad...");
2505 MMWFDSINK_PAD_PROBE(wfd_sink, pad, NULL, NULL);
2507 gst_pad_add_probe(pad,
2508 GST_PAD_PROBE_TYPE_BUFFER,
2509 _mm_wfd_sink_check_running_time,
2513 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_video_pipeline(wfd_sink, &sinkpad)) {
2514 wfd_sink_error("failed to prepare video pipeline....");
2517 } else if (name[0] == 'a') {
2518 wfd_sink_debug("=========== >>>>>>>>>> Received AUDIO pad...");
2520 MMWFDSINK_PAD_PROBE(wfd_sink, pad, NULL, NULL);
2522 gst_pad_add_probe(pad,
2523 GST_PAD_PROBE_TYPE_BUFFER,
2524 _mm_wfd_sink_check_running_time,
2528 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_audio_pipeline(wfd_sink, &sinkpad)) {
2529 wfd_sink_error("failed to prepare audio pipeline....");
2533 wfd_sink_error("unexceptable pad is added!!!");
2538 wfd_sink_debug("try to link %s:%s and %s:%s",
2539 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
2540 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
2541 if (GST_PAD_LINK_OK != gst_pad_link_full(srcpad, sinkpad, GST_PAD_LINK_CHECK_NOTHING)) {
2542 wfd_sink_error("failed to link %s:%s and %s:%s",
2543 GST_ELEMENT_NAME(GST_PAD_PARENT(srcpad)), GST_PAD_NAME(srcpad),
2544 GST_ELEMENT_NAME(GST_PAD_PARENT(sinkpad)), GST_PAD_NAME(sinkpad));
2549 MMWFDSINK_GENERATE_DOT_IF_ENABLED(wfd_sink, "video-pad-added-pipeline");
2550 else if (name[0] == 'a')
2551 MMWFDSINK_GENERATE_DOT_IF_ENABLED(wfd_sink, "audio-pad-added-pipeline");
2554 MMWFDSINK_FREEIF(name);
2557 gst_object_unref(GST_OBJECT(srcpad));
2561 gst_object_unref(GST_OBJECT(sinkpad));
2564 wfd_sink_debug_fleave();
2570 /* need to notify to app */
2571 MMWFDSINK_POST_MESSAGE(wfd_sink,
2572 MM_ERROR_WFD_INTERNAL,
2573 MMWFDSINK_CURRENT_STATE(wfd_sink));
2579 __mm_wfd_sink_change_av_format(GstElement *wfdsrc, gpointer *need_to_flush, gpointer data)
2581 mm_wfd_sink_t *wfd_sink = (mm_wfd_sink_t *)data;
2583 wfd_sink_debug_fenter();
2585 wfd_sink_return_if_fail(wfd_sink);
2586 wfd_sink_return_if_fail(need_to_flush);
2588 if (MMWFDSINK_CURRENT_STATE(wfd_sink) == MM_WFD_SINK_STATE_PLAYING) {
2589 wfd_sink_debug("need to flush pipeline");
2590 *need_to_flush = (gpointer) TRUE;
2592 wfd_sink_debug("don't need to flush pipeline");
2593 *need_to_flush = (gpointer) FALSE;
2597 wfd_sink_debug_fleave();
2602 __mm_wfd_sink_update_stream_info(GstElement *wfdsrc, GstStructure *str, gpointer data)
2604 mm_wfd_sink_t *wfd_sink = (mm_wfd_sink_t *)data;
2605 MMWFDSinkStreamInfo *stream_info = NULL;
2606 gint is_valid_audio_format = FALSE;
2607 gint is_valid_video_format = FALSE;
2608 gchar *audio_format;
2609 gchar *video_format;
2611 wfd_sink_debug_fenter();
2612 wfd_sink_return_if_fail(str && GST_IS_STRUCTURE(str));
2613 wfd_sink_return_if_fail(wfd_sink);
2615 stream_info = &wfd_sink->stream_info;
2617 if (gst_structure_has_field(str, "audio_format")) {
2618 is_valid_audio_format = TRUE;
2619 audio_format = g_strdup(gst_structure_get_string(str, "audio_format"));
2620 if (g_strrstr(audio_format, "AAC"))
2621 stream_info->audio_stream_info.codec = MM_WFD_SINK_AUDIO_CODEC_AAC;
2622 else if (g_strrstr(audio_format, "AC3"))
2623 stream_info->audio_stream_info.codec = MM_WFD_SINK_AUDIO_CODEC_AC3;
2624 else if (g_strrstr(audio_format, "LPCM"))
2625 stream_info->audio_stream_info.codec = MM_WFD_SINK_AUDIO_CODEC_LPCM;
2627 wfd_sink_error("invalid audio format(%s)...", audio_format);
2628 is_valid_audio_format = FALSE;
2630 if (is_valid_audio_format == TRUE) {
2631 if (gst_structure_has_field(str, "audio_rate"))
2632 gst_structure_get_int(str, "audio_rate", &stream_info->audio_stream_info.sample_rate);
2633 if (gst_structure_has_field(str, "audio_channels"))
2634 gst_structure_get_int(str, "audio_channels", &stream_info->audio_stream_info.channels);
2635 if (gst_structure_has_field(str, "audio_bitwidth"))
2636 gst_structure_get_int(str, "audio_bitwidth", &stream_info->audio_stream_info.bitwidth);
2638 WFD_SINK_MANAGER_APPEND_CMD(wfd_sink, WFD_SINK_MANAGER_CMD_PREPARE_A_PIPELINE);
2640 wfd_sink_debug("audio_format : %s \n \t rate : %d \n \t channels : %d \n \t bitwidth : %d \n \t \n",
2642 stream_info->audio_stream_info.sample_rate,
2643 stream_info->audio_stream_info.channels,
2644 stream_info->audio_stream_info.bitwidth);
2647 g_free(audio_format);
2649 if (gst_structure_has_field(str, "video_format")) {
2650 is_valid_video_format = TRUE;
2651 video_format = g_strdup(gst_structure_get_string(str, "video_format"));
2652 if (g_strrstr(video_format, "H264")) {
2653 stream_info->video_stream_info.codec = MM_WFD_SINK_VIDEO_CODEC_H264;
2654 } else if (g_strrstr(video_format, "H265")) {
2655 stream_info->video_stream_info.codec = MM_WFD_SINK_VIDEO_CODEC_H265;
2657 wfd_sink_error("invalid video format(%s)...", video_format);
2658 is_valid_video_format = FALSE;
2661 if (is_valid_video_format == TRUE) {
2662 if (gst_structure_has_field(str, "video_width"))
2663 gst_structure_get_int(str, "video_width", &stream_info->video_stream_info.width);
2664 if (gst_structure_has_field(str, "video_height"))
2665 gst_structure_get_int(str, "video_height", &stream_info->video_stream_info.height);
2666 if (gst_structure_has_field(str, "video_framerate"))
2667 gst_structure_get_int(str, "video_framerate", &stream_info->video_stream_info.frame_rate);
2669 WFD_SINK_MANAGER_APPEND_CMD(wfd_sink, WFD_SINK_MANAGER_CMD_PREPARE_V_PIPELINE);
2671 wfd_sink_debug("video_format : %s \n \t width : %d \n \t height : %d \n \t frame_rate : %d \n \t",
2673 stream_info->video_stream_info.width,
2674 stream_info->video_stream_info.height,
2675 stream_info->video_stream_info.frame_rate);
2678 g_free(video_format);
2681 WFD_SINK_MANAGER_SIGNAL_CMD(wfd_sink);
2683 wfd_sink_debug_fleave();
2686 static void __mm_wfd_sink_prepare_video_resolution(gint resolution, guint64 *CEA_resolution,
2687 guint64 *VESA_resolution, guint64 *HH_resolution)
2689 if (resolution == MM_WFD_SINK_RESOLUTION_UNKNOWN) return;
2691 *CEA_resolution = 0;
2692 *VESA_resolution = 0;
2695 if (resolution & MM_WFD_SINK_RESOLUTION_1920x1080_P30)
2696 *CEA_resolution |= WFD_CEA_1920x1080P30;
2698 if (resolution & MM_WFD_SINK_RESOLUTION_1280x720_P30)
2699 *CEA_resolution |= WFD_CEA_1280x720P30;
2701 if (resolution & MM_WFD_SINK_RESOLUTION_960x540_P30)
2702 *HH_resolution |= WFD_HH_960x540P30;
2704 if (resolution & MM_WFD_SINK_RESOLUTION_864x480_P30)
2705 *HH_resolution |= WFD_HH_864x480P30;
2707 if (resolution & MM_WFD_SINK_RESOLUTION_720x480_P60)
2708 *CEA_resolution |= WFD_CEA_720x480P60;
2710 if (resolution & MM_WFD_SINK_RESOLUTION_640x480_P60)
2711 *CEA_resolution |= WFD_CEA_640x480P60;
2713 if (resolution & MM_WFD_SINK_RESOLUTION_640x360_P30)
2714 *HH_resolution |= WFD_HH_640x360P30;
2717 static int __mm_wfd_sink_prepare_source(mm_wfd_sink_t *wfd_sink, GstElement *wfdsrc)
2719 GstStructure *wfd_audio_codecs = NULL;
2720 GstStructure *wfd_video_formats = NULL;
2721 GstStructure *wfd_content_protection = NULL;
2722 GstStructure *wfd2_video_formats = NULL;
2723 GstStructure *wfd2_audio_codecs = NULL;
2724 gint hdcp_version = 0;
2726 guint64 CEA_resolution = 0;
2727 guint64 VESA_resolution = 0;
2728 guint64 HH_resolution = 0;
2729 GObjectClass *klass;
2731 wfd_sink_debug_fenter();
2733 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
2734 wfd_sink_return_val_if_fail(wfd_sink->attrs, MM_ERROR_WFD_NOT_INITIALIZED);
2735 wfd_sink_return_val_if_fail(wfdsrc, MM_ERROR_WFD_NOT_INITIALIZED);
2737 klass = G_OBJECT_GET_CLASS(G_OBJECT(wfdsrc));
2739 if (g_object_class_find_property(klass, "enable-pad-probe")) /* for common wfdsrc */
2740 g_object_set(G_OBJECT(wfdsrc), "enable-pad-probe", wfd_sink->ini.trace_buffers_of_wfdsrc, NULL);
2741 g_object_set(G_OBJECT(wfdsrc), "udp-buffer-size", 2097152, NULL);
2742 g_object_set(G_OBJECT(wfdsrc), "latency", wfd_sink->ini.jitter_buffer_latency, NULL);
2743 g_object_set(G_OBJECT(wfdsrc), "user-agent", wfd_sink->ini.user_agent, NULL);
2744 g_object_set(G_OBJECT(wfdsrc), "dump-rtsp-message", wfd_sink->ini.dump_rtsp_message, NULL);
2745 if (g_object_class_find_property(klass, "dump-rtp-data"))
2746 g_object_set(G_OBJECT(wfdsrc), "dump-rtp-data", wfd_sink->ini.dump_rtp_data, NULL);
2747 if (g_object_class_find_property(klass, "trace-first-buffer"))
2748 g_object_set(G_OBJECT(wfdsrc), "trace-first-buffer", wfd_sink->ini.trace_first_buffer, NULL);
2749 if (g_object_class_find_property(klass, "trace-buffers"))
2750 g_object_set(G_OBJECT(wfdsrc), "trace-buffers", wfd_sink->ini.trace_buffers, NULL);
2751 if (g_object_class_find_property(klass, "do-request"))
2752 g_object_set(G_OBJECT(wfdsrc), "do-request", wfd_sink->ini.enable_retransmission, NULL);
2754 /* set audio parameter for Wi-Fi Display session negotiation */
2755 wfd_audio_codecs = gst_structure_new("wfd_audio_codecs",
2756 "audio_codec", G_TYPE_UINT, wfd_sink->ini.wfd_audio_codecs.audio_codec,
2757 "audio_latency", G_TYPE_UINT, wfd_sink->ini.wfd_audio_codecs.audio_latency,
2758 "audio_channels", G_TYPE_UINT, wfd_sink->ini.wfd_audio_codecs.audio_channel,
2759 "audio_sampling_frequency", G_TYPE_UINT, wfd_sink->ini.wfd_audio_codecs.audio_sampling_frequency,
2762 if (wfd_audio_codecs) {
2763 g_object_set(G_OBJECT(wfdsrc), "wfd-audio-codecs", wfd_audio_codecs, NULL);
2764 gst_structure_free(wfd_audio_codecs);
2765 wfd_audio_codecs = NULL;
2768 /* set video parameter for Wi-Fi Display session negotiation */
2769 CEA_resolution = wfd_sink->ini.wfd_video_formats.video_cea_support;
2770 VESA_resolution = wfd_sink->ini.wfd_video_formats.video_vesa_support;
2771 HH_resolution = wfd_sink->ini.wfd_video_formats.video_hh_support;
2773 __mm_wfd_sink_prepare_video_resolution(wfd_sink->supportive_resolution,
2774 &CEA_resolution, &VESA_resolution, &HH_resolution);
2775 wfd_video_formats = gst_structure_new("wfd_video_formats",
2776 "video_codec", G_TYPE_UINT, wfd_sink->ini.wfd_video_formats.video_codec,
2777 "video_native_resolution", G_TYPE_UINT, wfd_sink->ini.wfd_video_formats.video_native_resolution,
2778 "video_cea_support", G_TYPE_UINT64, CEA_resolution,
2779 "video_vesa_support", G_TYPE_UINT64, VESA_resolution,
2780 "video_hh_support", G_TYPE_UINT64, HH_resolution,
2781 "video_profile", G_TYPE_UINT, wfd_sink->ini.wfd_video_formats.video_profile,
2782 "video_level", G_TYPE_UINT, wfd_sink->ini.wfd_video_formats.video_level,
2783 "video_latency", G_TYPE_UINT, wfd_sink->ini.wfd_video_formats.video_latency,
2784 "video_vertical_resolution", G_TYPE_INT, wfd_sink->ini.wfd_video_formats.video_vertical_resolution,
2785 "video_horizontal_resolution", G_TYPE_INT, wfd_sink->ini.wfd_video_formats.video_horizontal_resolution,
2786 "video_minimum_slicing", G_TYPE_INT, wfd_sink->ini.wfd_video_formats.video_minimum_slicing,
2787 "video_slice_enc_param", G_TYPE_INT, wfd_sink->ini.wfd_video_formats.video_slice_enc_param,
2788 "video_framerate_control_support", G_TYPE_INT, wfd_sink->ini.wfd_video_formats.video_framerate_control_support,
2791 if (wfd_video_formats) {
2792 g_object_set(G_OBJECT(wfdsrc), "wfd-video-formats", wfd_video_formats, NULL);
2793 gst_structure_free(wfd_video_formats);
2794 wfd_video_formats = NULL;
2798 /* set hdcp parameter for Wi-Fi Display session negotiation */
2799 if (wfd_sink->ini.wfd_content_protection.enable_hdcp) {
2800 mm_attrs_get_int_by_name(wfd_sink->attrs, "hdcp_version", &hdcp_version);
2801 mm_attrs_get_int_by_name(wfd_sink->attrs, "hdcp_port", &hdcp_port);
2802 wfd_sink_debug("set hdcp version %d with %d port", hdcp_version, hdcp_port);
2804 wfd_content_protection = gst_structure_new("wfd_content_protection",
2805 "hdcp_version", G_TYPE_INT, hdcp_version,
2806 "hdcp_port_no", G_TYPE_INT, hdcp_port,
2809 if (wfd_content_protection) {
2810 g_object_set(G_OBJECT(wfdsrc), "wfd-content-protection", wfd_content_protection, NULL);
2811 gst_structure_free(wfd_content_protection);
2812 wfd_content_protection = NULL;
2816 if (g_object_class_find_property(klass, "wfd2-audio-codecs")) {
2817 /* set audio parameter for Wi-Fi Display R2 session negotiation */
2818 wfd2_audio_codecs = gst_structure_new("wfd2-audio-codecs",
2819 "audio_codec", G_TYPE_UINT, wfd_sink->ini.wfd2_audio_codecs.audio_codec,
2820 "audio_lpcm_mode", G_TYPE_UINT, wfd_sink->ini.wfd2_audio_codecs.audio_lpcm_mode,
2821 "audio_aac_mode", G_TYPE_UINT, wfd_sink->ini.wfd2_audio_codecs.audio_aac_mode,
2822 "audio_ac3_mode", G_TYPE_UINT, wfd_sink->ini.wfd2_audio_codecs.audio_ac3_mode,
2825 if (wfd2_audio_codecs) {
2826 g_object_set(G_OBJECT(wfdsrc), "wfd2-audio-codecs", wfd2_audio_codecs, NULL);
2827 gst_structure_free(wfd2_audio_codecs);
2828 wfd2_audio_codecs = NULL;
2832 if (g_object_class_find_property(klass, "wfd2-video-format-h264")) {
2833 /* set video parameter for Wi-Fi Display R2 session negotiation */
2834 CEA_resolution = wfd_sink->ini.wfd2_video_h264_info.video_cea_support;
2835 VESA_resolution = wfd_sink->ini.wfd2_video_h264_info.video_vesa_support;
2836 HH_resolution = wfd_sink->ini.wfd2_video_h264_info.video_hh_support;
2838 if (wfd_sink->ini.wfd2_video_formats.video_codec & WFD_VIDEO_H264) {
2839 wfd2_video_formats = gst_structure_new("wfd2-video-format-h264",
2840 "video_codec", G_TYPE_UINT, WFD_VIDEO_H264,
2841 "video_native_resolution", G_TYPE_UINT, wfd_sink->ini.wfd2_video_formats.video_native_resolution,
2842 "video_cea_support", G_TYPE_UINT64, CEA_resolution,
2843 "video_vesa_support", G_TYPE_UINT64, VESA_resolution,
2844 "video_hh_support", G_TYPE_UINT64, HH_resolution,
2845 "video_profile", G_TYPE_UINT, wfd_sink->ini.wfd2_video_h264_info.video_profile,
2846 "video_level", G_TYPE_UINT, wfd_sink->ini.wfd2_video_h264_info.video_level,
2847 "video_latency", G_TYPE_UINT, wfd_sink->ini.wfd2_video_h264_info.video_latency,
2848 "video_vertical_resolution", G_TYPE_INT, wfd_sink->ini.wfd2_video_h264_info.video_vertical_resolution,
2849 "video_horizontal_resolution", G_TYPE_INT, wfd_sink->ini.wfd2_video_h264_info.video_horizontal_resolution,
2850 "video_minimum_slicing", G_TYPE_INT, wfd_sink->ini.wfd2_video_h264_info.video_minimum_slicing,
2851 "video_slice_enc_param", G_TYPE_INT, wfd_sink->ini.wfd2_video_h264_info.video_slice_enc_param,
2852 "video_framerate_control_support", G_TYPE_INT, wfd_sink->ini.wfd2_video_h264_info.video_framerate_control_support,
2853 "video_non_transcoding_support", G_TYPE_INT, wfd_sink->ini.wfd2_video_formats.video_non_transcoding_support,
2856 if (wfd2_video_formats) {
2857 g_object_set(G_OBJECT(wfdsrc), "wfd2-video-format-h264", wfd2_video_formats, NULL);
2858 gst_structure_free(wfd2_video_formats);
2859 wfd2_video_formats = NULL;
2864 if (g_object_class_find_property(klass, "wfd2-video-format-h265")) {
2865 /* set video parameter for Wi-Fi Display R2 session negotiation */
2866 CEA_resolution = wfd_sink->ini.wfd2_video_h265_info.video_cea_support;
2867 VESA_resolution = wfd_sink->ini.wfd2_video_h265_info.video_vesa_support;
2868 HH_resolution = wfd_sink->ini.wfd2_video_h265_info.video_hh_support;
2870 if (wfd_sink->ini.wfd2_video_formats.video_codec & WFD_VIDEO_H265) {
2871 wfd2_video_formats = gst_structure_new("wfd2-video-format-h265",
2872 "video_codec", G_TYPE_UINT, WFD_VIDEO_H265,
2873 "video_native_resolution", G_TYPE_UINT, wfd_sink->ini.wfd2_video_formats.video_native_resolution,
2874 "video_cea_support", G_TYPE_UINT64, CEA_resolution,
2875 "video_vesa_support", G_TYPE_UINT64, VESA_resolution,
2876 "video_hh_support", G_TYPE_UINT64, HH_resolution,
2877 "video_profile", G_TYPE_UINT, wfd_sink->ini.wfd2_video_h265_info.video_profile,
2878 "video_level", G_TYPE_UINT, wfd_sink->ini.wfd2_video_h265_info.video_level,
2879 "video_latency", G_TYPE_UINT, wfd_sink->ini.wfd2_video_h265_info.video_latency,
2880 "video_vertical_resolution", G_TYPE_INT, wfd_sink->ini.wfd2_video_h265_info.video_vertical_resolution,
2881 "video_horizontal_resolution", G_TYPE_INT, wfd_sink->ini.wfd2_video_h265_info.video_horizontal_resolution,
2882 "video_minimum_slicing", G_TYPE_INT, wfd_sink->ini.wfd2_video_h265_info.video_minimum_slicing,
2883 "video_slice_enc_param", G_TYPE_INT, wfd_sink->ini.wfd2_video_h265_info.video_slice_enc_param,
2884 "video_framerate_control_support", G_TYPE_INT, wfd_sink->ini.wfd2_video_h265_info.video_framerate_control_support,
2885 "video_non_transcoding_support", G_TYPE_INT, wfd_sink->ini.wfd2_video_formats.video_non_transcoding_support,
2888 if (wfd2_video_formats) {
2889 g_object_set(G_OBJECT(wfdsrc), "wfd2-video-format-h265", wfd2_video_formats, NULL);
2890 gst_structure_free(wfd2_video_formats);
2891 wfd2_video_formats = NULL;
2896 wfd_sink->update_stream_info_sig_id = g_signal_connect(wfdsrc, "update-media-info",
2897 G_CALLBACK(__mm_wfd_sink_update_stream_info), wfd_sink);
2899 wfd_sink->change_av_format_sig_id = g_signal_connect(wfdsrc, "change-av-format",
2900 G_CALLBACK(__mm_wfd_sink_change_av_format), wfd_sink);
2902 wfd_sink_debug_fleave();
2904 return MM_ERROR_NONE;
2907 static int __mm_wfd_sink_prepare_demux(mm_wfd_sink_t *wfd_sink, GstElement *demux)
2909 wfd_sink_debug_fenter();
2911 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
2912 wfd_sink_return_val_if_fail(demux, MM_ERROR_WFD_NOT_INITIALIZED);
2914 g_signal_connect(demux, "pad-added",
2915 G_CALLBACK(__mm_wfd_sink_demux_pad_added), wfd_sink);
2917 wfd_sink_debug_fleave();
2919 return MM_ERROR_NONE;
2922 static void __mm_wfd_sink_queue_overrun(GstElement *queue, gpointer u_data)
2926 wfd_sink_debug_fenter();
2928 return_if_fail(queue);
2930 g_object_get(G_OBJECT(queue), "current-level-time", &time, NULL);
2932 wfd_sink_warning("%s is overrun(%" GST_TIME_FORMAT")",
2933 GST_ELEMENT_NAME(queue), GST_TIME_ARGS(time));
2935 wfd_sink_debug_fleave();
2940 static void __mm_wfd_sink_prepare_queue(mm_wfd_sink_t *wfd_sink, GstElement *queue)
2942 wfd_sink_debug_fenter();
2944 wfd_sink_return_if_fail(wfd_sink);
2945 wfd_sink_return_if_fail(queue);
2947 /* set maximum buffer size of queue as 3sec */
2948 g_object_set(G_OBJECT(queue), "max-size-bytes", 0, NULL);
2949 g_object_set(G_OBJECT(queue), "max-size-buffers", 0, NULL);
2950 g_object_set(G_OBJECT(queue), "max-size-time", (guint64)3000000000ULL, NULL);
2951 g_signal_connect(queue, "overrun",
2952 G_CALLBACK(__mm_wfd_sink_queue_overrun), wfd_sink);
2954 wfd_sink_debug_fleave();
2960 int __mm_wfd_sink_create_pipeline(mm_wfd_sink_t *wfd_sink)
2962 MMWFDSinkGstElement *mainbin = NULL;
2963 GList *element_bucket = NULL;
2967 wfd_sink_debug_fenter();
2969 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
2970 wfd_sink_return_val_if_fail(wfd_sink->attrs, MM_ERROR_WFD_NOT_INITIALIZED);
2972 /* Create pipeline */
2973 wfd_sink->pipeline = (MMWFDSinkGstPipelineInfo *) g_malloc0(sizeof(MMWFDSinkGstPipelineInfo));
2974 if (wfd_sink->pipeline == NULL)
2977 memset(wfd_sink->pipeline, 0, sizeof(MMWFDSinkGstPipelineInfo));
2979 /* create mainbin */
2980 mainbin = (MMWFDSinkGstElement *) g_malloc0(sizeof(MMWFDSinkGstElement) * WFD_SINK_M_NUM);
2981 if (mainbin == NULL)
2984 memset(mainbin, 0, sizeof(MMWFDSinkGstElement) * WFD_SINK_M_NUM);
2986 /* create pipeline */
2987 mainbin[WFD_SINK_M_PIPE].id = WFD_SINK_M_PIPE;
2988 mainbin[WFD_SINK_M_PIPE].gst = gst_pipeline_new("wfdsink");
2989 if (!mainbin[WFD_SINK_M_PIPE].gst) {
2990 wfd_sink_error("failed to create pipeline");
2995 MMWFDSINK_CREATE_ELEMENT(mainbin, WFD_SINK_M_SRC, wfd_sink->ini.name_of_source, "wfdsink_source", TRUE);
2996 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_SRC].gst, "src");
2997 if (mainbin[WFD_SINK_M_SRC].gst) {
2998 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_source(wfd_sink, mainbin[WFD_SINK_M_SRC].gst)) {
2999 wfd_sink_error("failed to prepare wfdsrc...");
3004 /* create rtpmp2tdepay */
3005 MMWFDSINK_CREATE_ELEMENT(mainbin, WFD_SINK_M_DEPAY, "rtpmp2tdepay", "wfdsink_depay", TRUE);
3006 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_DEPAY].gst, "src");
3007 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_DEPAY].gst, "sink");
3009 MMWFDSINK_TS_DATA_DUMP(wfd_sink, mainbin[WFD_SINK_M_DEPAY].gst, "src");
3011 /* create queue for ts */
3012 MMWFDSINK_CREATE_ELEMENT(mainbin, WFD_SINK_M_QUEUE, "queue", "ts_queue", TRUE);
3013 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_QUEUE].gst, "src");
3014 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_QUEUE].gst, "sink");
3015 g_object_set(G_OBJECT(mainbin[WFD_SINK_M_QUEUE].gst), "max-size-buffers", 200000, NULL);
3017 /* create valve for demux */
3018 MMWFDSINK_CREATE_ELEMENT(mainbin, WFD_SINK_M_D_VALVE, "valve", "demux_valve", TRUE);
3019 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_D_VALVE].gst, "src");
3020 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_D_VALVE].gst, "sink");
3022 /* create tsdemuxer*/
3023 MMWFDSINK_CREATE_ELEMENT(mainbin, WFD_SINK_M_DEMUX, wfd_sink->ini.name_of_tsdemux, "wfdsink_demux", TRUE);
3024 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_DEMUX].gst, "sink");
3025 if (mainbin[WFD_SINK_M_DEMUX].gst) {
3026 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_demux(wfd_sink, mainbin[WFD_SINK_M_DEMUX].gst)) {
3027 wfd_sink_error("failed to prepare demux...");
3032 /* create valve for audio */
3033 MMWFDSINK_CREATE_ELEMENT(mainbin, WFD_SINK_M_A_VALVE, "valve", "audio_valve", FALSE);
3034 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_A_VALVE].gst, "src");
3035 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_A_VALVE].gst, "sink");
3037 /* create valve for video */
3038 MMWFDSINK_CREATE_ELEMENT(mainbin, WFD_SINK_M_V_VALVE, "valve", "video_valve", FALSE);
3039 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_V_VALVE].gst, "src");
3040 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_V_VALVE].gst, "sink");
3042 /* adding created elements to pipeline */
3043 if (!__mm_wfd_sink_gst_element_add_bucket_to_bin(GST_BIN_CAST(mainbin[WFD_SINK_M_PIPE].gst), element_bucket, FALSE)) {
3044 wfd_sink_error("failed to add elements");
3048 /* linking elements in the bucket by added order. */
3049 if (__mm_wfd_sink_gst_element_link_bucket(element_bucket) == -1) {
3050 wfd_sink_error("failed to link elements");
3054 /* connect bus callback */
3055 bus = gst_pipeline_get_bus(GST_PIPELINE(mainbin[WFD_SINK_M_PIPE].gst));
3057 wfd_sink_error("cannot get bus from pipeline.");
3061 /* add bus message callback*/
3062 wfd_sink->msg_callback_id = gst_bus_add_watch(bus, (GstBusFunc)_mm_wfd_sink_msg_callback, wfd_sink);
3064 /* set sync handler to get tag synchronously */
3065 gst_bus_set_sync_handler(bus, _mm_wfd_bus_sync_callback, wfd_sink, NULL);
3067 g_list_free(element_bucket);
3068 element_bucket = NULL;
3069 gst_object_unref(GST_OBJECT(bus));
3072 /* now we have completed mainbin. take it */
3073 wfd_sink->pipeline->mainbin = mainbin;
3075 wfd_sink_debug_fleave();
3077 return MM_ERROR_NONE;
3081 wfd_sink_error("ERROR : releasing pipeline");
3083 if (element_bucket) {
3084 g_list_free(element_bucket);
3085 element_bucket = NULL;
3088 /* release element which are not added to bin */
3089 for (i = 1; i < WFD_SINK_M_NUM; i++) { /* NOTE : skip pipeline */
3090 if (mainbin != NULL && mainbin[i].gst) {
3091 GstObject *parent = NULL;
3092 parent = gst_element_get_parent(mainbin[i].gst);
3095 gst_object_unref(GST_OBJECT(mainbin[i].gst));
3096 mainbin[i].gst = NULL;
3098 gst_object_unref(GST_OBJECT(parent));
3104 /* release mainbin with it's childs */
3105 if (mainbin != NULL && mainbin[WFD_SINK_M_PIPE].gst) {
3106 gst_object_unref(GST_OBJECT(mainbin[WFD_SINK_M_PIPE].gst));
3107 mainbin[WFD_SINK_M_PIPE].gst = NULL;
3110 MMWFDSINK_FREEIF(mainbin);
3112 MMWFDSINK_FREEIF(wfd_sink->pipeline);
3114 return MM_ERROR_WFD_INTERNAL;
3117 int __mm_wfd_sink_link_audio_decodebin(mm_wfd_sink_t *wfd_sink)
3119 MMWFDSinkGstElement *a_decodebin = NULL;
3120 MMWFDSinkGstElement *first_element = NULL;
3121 MMWFDSinkGstElement *last_element = NULL;
3122 GList *element_bucket = NULL;
3123 GstPad *sinkpad = NULL;
3124 GstPad *srcpad = NULL;
3125 GstPad *ghostpad = NULL;
3126 GList *list_temp = NULL;
3128 wfd_sink_debug_fenter();
3130 wfd_sink_return_val_if_fail(wfd_sink &&
3131 wfd_sink->pipeline &&
3132 wfd_sink->pipeline->a_decodebin &&
3133 wfd_sink->pipeline->a_decodebin[WFD_SINK_A_D_BIN].gst,
3134 MM_ERROR_WFD_NOT_INITIALIZED);
3136 if (wfd_sink->audio_decodebin_is_linked) {
3137 wfd_sink_debug("audio decodebin is already linked... nothing to do");
3138 return MM_ERROR_NONE;
3141 if (wfd_sink->stream_info.audio_stream_info.codec == MM_WFD_SINK_AUDIO_CODEC_NONE) {
3142 wfd_sink_debug("Skip link audio decodebin for none audio codec.");
3143 wfd_sink_debug_fleave();
3144 return MM_ERROR_NONE;
3147 /* take audio decodebin */
3148 a_decodebin = wfd_sink->pipeline->a_decodebin;
3150 /* check audio queue */
3151 if (a_decodebin[WFD_SINK_A_D_QUEUE].gst)
3152 element_bucket = g_list_append(element_bucket, &a_decodebin[WFD_SINK_A_D_QUEUE]);
3154 /* check audio hdcp */
3155 if (a_decodebin[WFD_SINK_A_D_HDCP].gst)
3156 element_bucket = g_list_append(element_bucket, &a_decodebin[WFD_SINK_A_D_HDCP]);
3158 /* check audio codec */
3159 switch (wfd_sink->stream_info.audio_stream_info.codec) {
3160 case MM_WFD_SINK_AUDIO_CODEC_LPCM:
3161 if (a_decodebin[WFD_SINK_A_D_LPCM_CONVERTER].gst)
3162 element_bucket = g_list_append(element_bucket, &a_decodebin[WFD_SINK_A_D_LPCM_CONVERTER]);
3163 if (a_decodebin[WFD_SINK_A_D_LPCM_FILTER].gst) {
3164 GstCaps *caps = NULL;
3165 element_bucket = g_list_append(element_bucket, &a_decodebin[WFD_SINK_A_D_LPCM_FILTER]);
3166 caps = gst_caps_new_simple("audio/x-raw",
3167 "rate", G_TYPE_INT, wfd_sink->stream_info.audio_stream_info.sample_rate,
3168 "channels", G_TYPE_INT, wfd_sink->stream_info.audio_stream_info.channels,
3169 "format", G_TYPE_STRING, "S16BE", NULL);
3171 g_object_set(G_OBJECT(a_decodebin[WFD_SINK_A_D_LPCM_CONVERTER].gst), "caps", caps, NULL);
3172 gst_object_unref(GST_OBJECT(caps));
3177 case MM_WFD_SINK_AUDIO_CODEC_AAC:
3178 if (a_decodebin[WFD_SINK_A_D_AAC_PARSE].gst)
3179 element_bucket = g_list_append(element_bucket, &a_decodebin[WFD_SINK_A_D_AAC_PARSE]);
3180 if (a_decodebin[WFD_SINK_A_D_AAC_DEC].gst)
3181 element_bucket = g_list_append(element_bucket, &a_decodebin[WFD_SINK_A_D_AAC_DEC]);
3184 case MM_WFD_SINK_AUDIO_CODEC_AC3:
3185 if (a_decodebin[WFD_SINK_A_D_AC3_PARSE].gst)
3186 element_bucket = g_list_append(element_bucket, &a_decodebin[WFD_SINK_A_D_AC3_PARSE]);
3187 if (a_decodebin[WFD_SINK_A_D_AC3_DEC].gst)
3188 element_bucket = g_list_append(element_bucket, &a_decodebin[WFD_SINK_A_D_AC3_DEC]);
3192 wfd_sink_error("audio codec is not decied yet. cannot link audio decodebin...");
3197 if (element_bucket == NULL) {
3198 wfd_sink_error("there are no elements to be linked in the audio decodebin, destroy it");
3199 if (MM_ERROR_NONE != __mm_wfd_sink_destroy_audio_decodebin(wfd_sink)) {
3200 wfd_sink_error("failed to destroy audio decodebin");
3206 /* adding elements to audio decodebin */
3207 if (!__mm_wfd_sink_gst_element_add_bucket_to_bin(GST_BIN_CAST(a_decodebin[WFD_SINK_A_D_BIN].gst), element_bucket, FALSE)) {
3208 wfd_sink_error("failed to add elements to audio decodebin");
3212 /* linking elements in the bucket by added order. */
3213 if (__mm_wfd_sink_gst_element_link_bucket(element_bucket) == -1) {
3214 wfd_sink_error("failed to link elements of the audio decodebin");
3218 /* get first element's sinkpad for creating ghostpad */
3219 list_temp = g_list_first(element_bucket);
3220 if (list_temp == NULL) {
3221 wfd_sink_error("failed to get first list of the element_bucket");
3225 first_element = (MMWFDSinkGstElement *)list_temp->data;
3226 if (!first_element) {
3227 wfd_sink_error("failed to get first element of the audio decodebin");
3231 sinkpad = gst_element_get_static_pad(GST_ELEMENT(first_element->gst), "sink");
3233 wfd_sink_error("failed to get sink pad from element(%s)",
3234 GST_STR_NULL(GST_ELEMENT_NAME(first_element->gst)));
3238 ghostpad = gst_ghost_pad_new("sink", sinkpad);
3240 wfd_sink_error("failed to create ghostpad of audio decodebin");
3244 if (FALSE == gst_element_add_pad(a_decodebin[WFD_SINK_A_D_BIN].gst, ghostpad)) {
3245 wfd_sink_error("failed to add ghostpad to audio decodebin");
3248 gst_object_unref(GST_OBJECT(sinkpad));
3252 /* get last element's src for creating ghostpad */
3253 list_temp = g_list_last(element_bucket);
3254 if (list_temp == NULL) {
3255 wfd_sink_error("failed to get last list of the element_bucket");
3259 last_element = (MMWFDSinkGstElement *)list_temp->data;
3260 if (!last_element) {
3261 wfd_sink_error("failed to get last element of the audio decodebin");
3265 srcpad = gst_element_get_static_pad(GST_ELEMENT(last_element->gst), "src");
3267 wfd_sink_error("failed to get src pad from element(%s)",
3268 GST_STR_NULL(GST_ELEMENT_NAME(last_element->gst)));
3272 ghostpad = gst_ghost_pad_new("src", srcpad);
3274 wfd_sink_error("failed to create ghostpad of audio decodebin");
3278 if (FALSE == gst_element_add_pad(a_decodebin[WFD_SINK_A_D_BIN].gst, ghostpad)) {
3279 wfd_sink_error("failed to add ghostpad to audio decodebin");
3282 gst_object_unref(GST_OBJECT(srcpad));
3285 g_list_free(element_bucket);
3288 wfd_sink->audio_decodebin_is_linked = TRUE;
3290 wfd_sink_debug_fleave();
3292 return MM_ERROR_NONE;
3297 gst_object_unref(GST_OBJECT(srcpad));
3302 gst_object_unref(GST_OBJECT(sinkpad));
3306 g_list_free(element_bucket);
3308 return MM_ERROR_WFD_INTERNAL;
3311 static int __mm_wfd_sink_prepare_audiosink(mm_wfd_sink_t *wfd_sink, GstElement *audio_sink)
3313 wfd_sink_debug_fenter();
3315 /* check audiosink is created */
3316 wfd_sink_return_val_if_fail(audio_sink, MM_ERROR_WFD_INVALID_ARGUMENT);
3317 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
3319 g_object_set(G_OBJECT(audio_sink), "provide-clock", FALSE, NULL);
3320 g_object_set(G_OBJECT(audio_sink), "buffer-time", 100000LL, NULL);
3321 g_object_set(G_OBJECT(audio_sink), "slave-method", 2, NULL);
3322 g_object_set(G_OBJECT(audio_sink), "async", wfd_sink->ini.audio_sink_async, NULL);
3323 g_object_set(G_OBJECT(audio_sink), "ts-offset", (gint64)wfd_sink->ini.sink_ts_offset, NULL);
3325 wfd_sink_debug_fleave();
3327 return MM_ERROR_NONE;
3330 int __mm_wfd_sink_destroy_audio_decodebin(mm_wfd_sink_t *wfd_sink)
3332 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
3333 MMWFDSinkGstElement *a_decodebin = NULL;
3334 GstObject *parent = NULL;
3337 wfd_sink_error_fenter();
3339 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
3341 if (wfd_sink->pipeline &&
3342 wfd_sink->pipeline->a_decodebin &&
3343 wfd_sink->pipeline->a_decodebin[WFD_SINK_A_D_BIN].gst) {
3344 a_decodebin = wfd_sink->pipeline->a_decodebin;
3346 wfd_sink_debug("audio decodebin is not created, nothing to destroy");
3347 return MM_ERROR_NONE;
3350 parent = gst_element_get_parent(a_decodebin[WFD_SINK_A_D_BIN].gst);
3352 wfd_sink_debug("audio decodebin has no parent.. need to relase by itself");
3354 if (GST_STATE(a_decodebin[WFD_SINK_A_D_BIN].gst) >= GST_STATE_READY) {
3355 wfd_sink_error("try to change state of audio decodebin to NULL");
3356 ret = gst_element_set_state(a_decodebin[WFD_SINK_A_D_BIN].gst, GST_STATE_NULL);
3357 if (ret != GST_STATE_CHANGE_SUCCESS) {
3358 wfd_sink_error("failed to change state of audio decodebin to NULL");
3359 return MM_ERROR_WFD_INTERNAL;
3363 /* release element which are not added to bin */
3364 for (i = 1; i < WFD_SINK_A_D_NUM; i++) { /* NOTE : skip bin */
3365 if (a_decodebin[i].gst) {
3366 parent = gst_element_get_parent(a_decodebin[i].gst);
3368 wfd_sink_debug("unref %s(current ref %d)",
3369 GST_STR_NULL(GST_ELEMENT_NAME(a_decodebin[i].gst)),
3370 ((GObject *) a_decodebin[i].gst)->ref_count);
3371 gst_object_unref(GST_OBJECT(a_decodebin[i].gst));
3372 a_decodebin[i].gst = NULL;
3374 wfd_sink_debug("%s has parent.(current ref %d)",
3375 GST_STR_NULL(GST_ELEMENT_NAME(a_decodebin[i].gst)),
3376 ((GObject *) a_decodebin[i].gst)->ref_count);
3377 gst_object_unref(GST_OBJECT(parent));
3383 /* release audio decodebin with it's childs */
3384 if (a_decodebin[WFD_SINK_A_D_BIN].gst) {
3385 gst_object_unref(GST_OBJECT(a_decodebin[WFD_SINK_A_D_BIN].gst));
3386 a_decodebin[WFD_SINK_A_D_BIN].gst = NULL;
3390 wfd_sink_debug("audio decodebin has parent(%s), unref it ",
3391 GST_STR_NULL(GST_OBJECT_NAME(GST_OBJECT(parent))));
3393 gst_object_unref(GST_OBJECT(parent));
3397 wfd_sink->audio_decodebin_is_linked = FALSE;
3399 MMWFDSINK_FREEIF(wfd_sink->pipeline->a_decodebin);
3401 wfd_sink_error_fleave();
3403 return MM_ERROR_NONE;
3406 int __mm_wfd_sink_create_audio_decodebin(mm_wfd_sink_t *wfd_sink)
3408 MMWFDSinkGstElement *a_decodebin = NULL;
3409 gint audio_codec = WFD_AUDIO_UNKNOWN;
3410 GList *element_bucket = NULL;
3411 gboolean link = TRUE;
3414 wfd_sink_debug_fenter();
3416 wfd_sink_return_val_if_fail(wfd_sink &&
3418 MM_ERROR_WFD_NOT_INITIALIZED);
3420 if (wfd_sink->pipeline->a_decodebin != NULL) {
3421 wfd_sink_error("The audio decode bin is already created.");
3422 return MM_ERROR_NONE;
3425 if (wfd_sink->stream_info.audio_stream_info.codec == MM_WFD_SINK_AUDIO_CODEC_NONE) {
3426 wfd_sink_debug("Skip create audio decodebin for none audio codec.");
3427 wfd_sink_debug_fleave();
3428 return MM_ERROR_NONE;
3431 /* check audio decodebin could be linked now */
3432 switch (wfd_sink->stream_info.audio_stream_info.codec) {
3433 case MM_WFD_SINK_AUDIO_CODEC_AAC:
3434 audio_codec = WFD_AUDIO_AAC;
3437 case MM_WFD_SINK_AUDIO_CODEC_AC3:
3438 audio_codec = WFD_AUDIO_AC3;
3441 case MM_WFD_SINK_AUDIO_CODEC_LPCM:
3442 audio_codec = WFD_AUDIO_LPCM;
3445 case MM_WFD_SINK_AUDIO_CODEC_NONE:
3447 wfd_sink_debug("audio decodebin could NOT be linked now, just create");
3448 audio_codec = wfd_sink->ini.wfd_audio_codecs.audio_codec;
3454 a_decodebin = (MMWFDSinkGstElement *)g_malloc0(sizeof(MMWFDSinkGstElement) * WFD_SINK_A_D_NUM);
3456 wfd_sink_error("failed to allocate memory for audio decodebin");
3457 return MM_ERROR_WFD_NO_FREE_SPACE;
3460 memset(a_decodebin, 0, sizeof(MMWFDSinkGstElement) * WFD_SINK_A_D_NUM);
3462 /* create audio decodebin */
3463 a_decodebin[WFD_SINK_A_D_BIN].id = WFD_SINK_A_D_BIN;
3464 a_decodebin[WFD_SINK_A_D_BIN].gst = gst_bin_new("audio_deocebin");
3465 if (!a_decodebin[WFD_SINK_A_D_BIN].gst) {
3466 wfd_sink_error("failed to create audio decodebin");
3471 MMWFDSINK_CREATE_ELEMENT(a_decodebin, WFD_SINK_A_D_QUEUE, "queue", "audio_queue", FALSE);
3472 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_QUEUE].gst, "sink");
3473 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_QUEUE].gst, "src");
3474 if (a_decodebin[WFD_SINK_A_D_QUEUE].gst)
3475 __mm_wfd_sink_prepare_queue(wfd_sink, a_decodebin[WFD_SINK_A_D_QUEUE].gst);
3478 MMWFDSINK_CREATE_ELEMENT(a_decodebin, WFD_SINK_A_D_HDCP, wfd_sink->ini.name_of_audio_hdcp, "audio_hdcp", FALSE);
3479 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_HDCP].gst, "sink");
3480 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_HDCP].gst, "src");
3483 audio_codec = wfd_sink->ini.wfd_audio_codecs.audio_codec;
3484 if (audio_codec & WFD_AUDIO_LPCM) {
3485 /* create LPCM converter */
3486 MMWFDSINK_CREATE_ELEMENT(a_decodebin, WFD_SINK_A_D_LPCM_CONVERTER, wfd_sink->ini.name_of_lpcm_converter, "audio_lpcm_convert", FALSE);
3487 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_LPCM_CONVERTER].gst, "sink");
3488 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_LPCM_CONVERTER].gst, "src");
3490 /* create LPCM filter */
3491 MMWFDSINK_CREATE_ELEMENT(a_decodebin, WFD_SINK_A_D_LPCM_FILTER, wfd_sink->ini.name_of_lpcm_filter, "audio_lpcm_filter", FALSE);
3492 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_LPCM_FILTER].gst, "sink");
3493 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_LPCM_FILTER].gst, "src");
3496 if (audio_codec & WFD_AUDIO_AAC) {
3497 /* create AAC parse */
3498 MMWFDSINK_CREATE_ELEMENT(a_decodebin, WFD_SINK_A_D_AAC_PARSE, wfd_sink->ini.name_of_aac_parser, "audio_aac_parser", FALSE);
3499 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_AAC_PARSE].gst, "sink");
3500 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_AAC_PARSE].gst, "src");
3502 /* create AAC decoder */
3503 MMWFDSINK_CREATE_ELEMENT(a_decodebin, WFD_SINK_A_D_AAC_DEC, wfd_sink->ini.name_of_aac_decoder, "audio_aac_dec", FALSE);
3504 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_AAC_DEC].gst, "sink");
3505 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_AAC_DEC].gst, "src");
3508 if (audio_codec & WFD_AUDIO_AC3) {
3509 /* create AC3 parser */
3510 MMWFDSINK_CREATE_ELEMENT(a_decodebin, WFD_SINK_A_D_AC3_PARSE, wfd_sink->ini.name_of_ac3_parser, "audio_ac3_parser", FALSE);
3511 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_AC3_PARSE].gst, "sink");
3512 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_AC3_PARSE].gst, "src");
3514 /* create AC3 decoder */
3515 MMWFDSINK_CREATE_ELEMENT(a_decodebin, WFD_SINK_A_D_AC3_DEC, wfd_sink->ini.name_of_ac3_decoder, "audio_ac3_dec", FALSE);
3516 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_AC3_DEC].gst, "sink");
3517 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_decodebin[WFD_SINK_A_D_AC3_DEC].gst, "src");
3520 g_list_free(element_bucket);
3523 wfd_sink->pipeline->a_decodebin = a_decodebin;
3525 /* link audio decodebin if audio codec is fixed */
3527 if (MM_ERROR_NONE != __mm_wfd_sink_link_audio_decodebin(wfd_sink)) {
3528 wfd_sink_error("failed to link audio decodebin, destroy audio decodebin");
3529 __mm_wfd_sink_destroy_audio_decodebin(wfd_sink);
3530 return MM_ERROR_WFD_INTERNAL;
3534 wfd_sink_debug_fleave();
3536 return MM_ERROR_NONE;
3539 wfd_sink_error("failed to create audio decodebin, release all");
3541 g_list_free(element_bucket);
3543 /* release element which are not added to bin */
3544 for (i = 1; i < WFD_SINK_A_D_NUM; i++) { /* NOTE : skip bin */
3545 if (a_decodebin != NULL && a_decodebin[i].gst) {
3546 GstObject *parent = NULL;
3547 parent = gst_element_get_parent(a_decodebin[i].gst);
3550 gst_object_unref(GST_OBJECT(a_decodebin[i].gst));
3551 a_decodebin[i].gst = NULL;
3553 gst_object_unref(GST_OBJECT(parent));
3559 /* release audio decodebin with it's childs */
3560 if (a_decodebin != NULL && a_decodebin[WFD_SINK_A_D_BIN].gst) {
3561 gst_object_unref(GST_OBJECT(a_decodebin[WFD_SINK_A_D_BIN].gst));
3562 a_decodebin[WFD_SINK_A_D_BIN].gst = NULL;
3565 MMWFDSINK_FREEIF(a_decodebin);
3567 return MM_ERROR_WFD_INTERNAL;
3570 int __mm_wfd_sink_destroy_audio_sinkbin(mm_wfd_sink_t *wfd_sink)
3572 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
3573 MMWFDSinkGstElement *a_sinkbin = NULL;
3574 GstObject *parent = NULL;
3577 wfd_sink_error_fenter();
3579 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
3581 if (wfd_sink->pipeline &&
3582 wfd_sink->pipeline->a_sinkbin &&
3583 wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_BIN].gst) {
3584 a_sinkbin = wfd_sink->pipeline->a_sinkbin;
3586 wfd_sink_debug("audio sinkbin is not created, nothing to destroy");
3587 return MM_ERROR_NONE;
3590 parent = gst_element_get_parent(a_sinkbin[WFD_SINK_A_S_BIN].gst);
3592 wfd_sink_error("audio sinkbin has no parent.. need to relase by itself");
3594 if (GST_STATE(a_sinkbin[WFD_SINK_A_S_BIN].gst) >= GST_STATE_READY) {
3595 wfd_sink_error("try to change state of audio sinkbin to NULL");
3596 ret = gst_element_set_state(a_sinkbin[WFD_SINK_A_S_BIN].gst, GST_STATE_NULL);
3597 if (ret != GST_STATE_CHANGE_SUCCESS) {
3598 wfd_sink_error("failed to change state of audio sinkbin to NULL");
3599 return MM_ERROR_WFD_INTERNAL;
3603 /* release element which are not added to bin */
3604 for (i = 1; i < WFD_SINK_A_S_NUM; i++) { /* NOTE : skip bin */
3605 if (a_sinkbin[i].gst) {
3606 parent = gst_element_get_parent(a_sinkbin[i].gst);
3608 wfd_sink_debug("unref %s(current ref %d)",
3609 GST_STR_NULL(GST_ELEMENT_NAME(a_sinkbin[i].gst)),
3610 ((GObject *) a_sinkbin[i].gst)->ref_count);
3611 gst_object_unref(GST_OBJECT(a_sinkbin[i].gst));
3612 a_sinkbin[i].gst = NULL;
3614 wfd_sink_debug("%s has parent.(current ref %d)",
3615 GST_STR_NULL(GST_ELEMENT_NAME(a_sinkbin[i].gst)),
3616 ((GObject *) a_sinkbin[i].gst)->ref_count);
3617 gst_object_unref(GST_OBJECT(parent));
3623 /* release audio sinkbin with it's childs */
3624 if (a_sinkbin[WFD_SINK_A_S_BIN].gst) {
3625 gst_object_unref(GST_OBJECT(a_sinkbin[WFD_SINK_A_S_BIN].gst));
3626 a_sinkbin[WFD_SINK_A_S_BIN].gst = NULL;
3629 wfd_sink_debug("audio sinkbin has parent(%s), unref it ",
3630 GST_STR_NULL(GST_OBJECT_NAME(GST_OBJECT(parent))));
3632 gst_object_unref(GST_OBJECT(parent));
3636 MMWFDSINK_FREEIF(wfd_sink->pipeline->a_sinkbin);
3638 wfd_sink_error_fleave();
3640 return MM_ERROR_NONE;
3643 int __mm_wfd_sink_create_audio_sinkbin(mm_wfd_sink_t *wfd_sink)
3645 MMWFDSinkGstElement *a_sinkbin = NULL;
3646 MMWFDSinkGstElement *first_element = NULL;
3647 GList *element_bucket = NULL;
3648 GstPad *ghostpad = NULL;
3651 GList *list_temp = NULL;
3653 wfd_sink_debug_fenter();
3655 wfd_sink_return_val_if_fail(wfd_sink &&
3657 MM_ERROR_WFD_NOT_INITIALIZED);
3659 if (wfd_sink->pipeline->a_sinkbin != NULL) {
3660 wfd_sink_error("The audio sink bin is already created.");
3661 return MM_ERROR_NONE;
3664 if (wfd_sink->stream_info.audio_stream_info.codec == MM_WFD_SINK_AUDIO_CODEC_NONE) {
3665 wfd_sink_error("Skip create audio sink bin for non audio codec.");
3666 wfd_sink_debug_fleave();
3667 return MM_ERROR_NONE;
3671 a_sinkbin = (MMWFDSinkGstElement *)g_malloc0(sizeof(MMWFDSinkGstElement) * WFD_SINK_A_S_NUM);
3673 wfd_sink_error("failed to allocate memory for audio sinkbin");
3674 return MM_ERROR_WFD_NO_FREE_SPACE;
3677 memset(a_sinkbin, 0, sizeof(MMWFDSinkGstElement) * WFD_SINK_A_S_NUM);
3679 /* create audio sinkbin */
3680 a_sinkbin[WFD_SINK_A_S_BIN].id = WFD_SINK_A_S_BIN;
3681 a_sinkbin[WFD_SINK_A_S_BIN].gst = gst_bin_new("audio_sinkbin");
3682 if (!a_sinkbin[WFD_SINK_A_S_BIN].gst) {
3683 wfd_sink_error("failed to create audio sinkbin");
3687 /* create resampler */
3688 MMWFDSINK_CREATE_ELEMENT(a_sinkbin, WFD_SINK_A_S_RESAMPLER,
3689 wfd_sink->ini.name_of_audio_resampler, "audio_resampler", TRUE);
3690 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_sinkbin[WFD_SINK_A_S_RESAMPLER].gst, "sink");
3691 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_sinkbin[WFD_SINK_A_S_RESAMPLER].gst, "src");
3694 MMWFDSINK_CREATE_ELEMENT(a_sinkbin, WFD_SINK_A_S_VOLUME,
3695 wfd_sink->ini.name_of_audio_volume, "audio_volume", TRUE);
3696 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_sinkbin[WFD_SINK_A_S_VOLUME].gst, "sink");
3697 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_sinkbin[WFD_SINK_A_S_VOLUME].gst, "src");
3699 MMWFDSINK_CREATE_ELEMENT(a_sinkbin, WFD_SINK_A_S_QUEUE,
3700 wfd_sink->ini.name_of_audio_sinkbin_queue, "audio_sinkbin_queue", TRUE);
3701 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_sinkbin[WFD_SINK_A_S_QUEUE].gst, "sink");
3702 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_sinkbin[WFD_SINK_A_S_QUEUE].gst, "src");
3705 MMWFDSINK_CREATE_ELEMENT(a_sinkbin, WFD_SINK_A_S_SINK,
3706 wfd_sink->ini.name_of_audio_sink, "audio_sink", TRUE);
3707 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, a_sinkbin[WFD_SINK_A_S_SINK].gst, "sink");
3708 if (a_sinkbin[WFD_SINK_A_S_SINK].gst) {
3709 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_audiosink(wfd_sink, a_sinkbin[WFD_SINK_A_S_SINK].gst)) {
3710 wfd_sink_error("failed to set audio sink property....");
3715 /* adding created elements to audio sinkbin */
3716 if (!__mm_wfd_sink_gst_element_add_bucket_to_bin(GST_BIN_CAST(a_sinkbin[WFD_SINK_A_S_BIN].gst), element_bucket, FALSE)) {
3717 wfd_sink_error("failed to add elements to audio sinkbin");
3721 /* linking elements in the bucket by added order. */
3722 if (__mm_wfd_sink_gst_element_link_bucket(element_bucket) == -1) {
3723 wfd_sink_error("failed to link elements fo the audio sinkbin");
3727 /* get first element's of the audio sinkbin */
3728 list_temp = g_list_first(element_bucket);
3729 if (list_temp == NULL) {
3730 wfd_sink_error("failed to get first list of the element_bucket");
3734 first_element = (MMWFDSinkGstElement *)list_temp->data;
3735 if (!first_element) {
3736 wfd_sink_error("failed to get first element of the audio sinkbin");
3740 /* get first element's sinkpad for creating ghostpad */
3741 pad = gst_element_get_static_pad(GST_ELEMENT(first_element->gst), "sink");
3743 wfd_sink_error("failed to get sink pad from element(%s)",
3744 GST_STR_NULL(GST_ELEMENT_NAME(first_element->gst)));
3748 ghostpad = gst_ghost_pad_new("sink", pad);
3750 wfd_sink_error("failed to create ghostpad of audio sinkbin");
3754 if (FALSE == gst_element_add_pad(a_sinkbin[WFD_SINK_A_S_BIN].gst, ghostpad)) {
3755 wfd_sink_error("failed to add ghostpad to audio sinkbin");
3758 gst_object_unref(GST_OBJECT(pad));
3761 g_list_free(element_bucket);
3762 element_bucket = NULL;
3765 wfd_sink->pipeline->a_sinkbin = a_sinkbin;
3767 wfd_sink_debug_fleave();
3769 return MM_ERROR_NONE;
3772 wfd_sink_error("failed to create audio sinkbin, releasing all");
3775 gst_object_unref(GST_OBJECT(pad));
3779 gst_object_unref(GST_OBJECT(ghostpad));
3782 if (element_bucket) {
3783 g_list_free(element_bucket);
3784 element_bucket = NULL;
3787 /* release element which are not added to bin */
3788 for (i = 1; i < WFD_SINK_A_S_NUM; i++) { /* NOTE : skip bin */
3789 if (a_sinkbin != NULL && a_sinkbin[i].gst) {
3790 GstObject *parent = NULL;
3791 parent = gst_element_get_parent(a_sinkbin[i].gst);
3794 gst_object_unref(GST_OBJECT(a_sinkbin[i].gst));
3795 a_sinkbin[i].gst = NULL;
3797 gst_object_unref(GST_OBJECT(parent));
3803 /* release audio sinkbin with it's childs */
3804 if (a_sinkbin != NULL && a_sinkbin[WFD_SINK_A_S_BIN].gst) {
3805 gst_object_unref(GST_OBJECT(a_sinkbin[WFD_SINK_A_S_BIN].gst));
3806 a_sinkbin[WFD_SINK_A_S_BIN].gst = NULL;
3808 MMWFDSINK_FREEIF(a_sinkbin);
3810 return MM_ERROR_WFD_INTERNAL;
3813 int __mm_wfd_sink_link_video_decodebin(mm_wfd_sink_t *wfd_sink)
3815 MMWFDSinkGstElement *v_decodebin = NULL;
3816 MMWFDSinkGstElement *first_element = NULL;
3817 MMWFDSinkGstElement *last_element = NULL;
3818 GList *element_bucket = NULL;
3819 GList *list_temp = NULL;
3820 GstPad *sinkpad = NULL;
3821 GstPad *srcpad = NULL;
3822 GstPad *ghostpad = NULL;
3824 wfd_sink_debug_fenter();
3826 wfd_sink_return_val_if_fail(wfd_sink &&
3827 wfd_sink->pipeline &&
3828 wfd_sink->pipeline->v_decodebin &&
3829 wfd_sink->pipeline->v_decodebin[WFD_SINK_V_D_BIN].gst,
3830 MM_ERROR_WFD_NOT_INITIALIZED);
3832 if (wfd_sink->video_decodebin_is_linked) {
3833 wfd_sink_debug("video decodebin is already linked... nothing to do");
3834 return MM_ERROR_NONE;
3837 if (wfd_sink->stream_info.video_stream_info.codec == MM_WFD_SINK_VIDEO_CODEC_NONE) {
3838 wfd_sink_debug("Skip link video decodebin for none video codec.");
3839 wfd_sink_debug_fleave();
3840 return MM_ERROR_NONE;
3843 /* take video decodebin */
3844 v_decodebin = wfd_sink->pipeline->v_decodebin;
3846 /* check video queue */
3847 if (v_decodebin[WFD_SINK_V_D_QUEUE].gst)
3848 element_bucket = g_list_append(element_bucket, &v_decodebin[WFD_SINK_V_D_QUEUE]);
3850 /* check video hdcp */
3851 if (v_decodebin[WFD_SINK_V_D_HDCP].gst)
3852 element_bucket = g_list_append(element_bucket, &v_decodebin[WFD_SINK_V_D_HDCP]);
3854 /* check video codec */
3855 switch (wfd_sink->stream_info.video_stream_info.codec) {
3856 case MM_WFD_SINK_VIDEO_CODEC_H264:
3857 if (v_decodebin[WFD_SINK_V_D_H264_PARSE].gst)
3858 element_bucket = g_list_append(element_bucket, &v_decodebin[WFD_SINK_V_D_H264_PARSE]);
3859 if (v_decodebin[WFD_SINK_V_D_H264_DEC].gst)
3860 element_bucket = g_list_append(element_bucket, &v_decodebin[WFD_SINK_V_D_H264_DEC]);
3863 case MM_WFD_SINK_VIDEO_CODEC_H265:
3864 if (v_decodebin[WFD_SINK_V_D_H265_PARSE].gst)
3865 element_bucket = g_list_append(element_bucket, &v_decodebin[WFD_SINK_V_D_H265_PARSE]);
3866 if (v_decodebin[WFD_SINK_V_D_H265_DEC].gst)
3867 element_bucket = g_list_append(element_bucket, &v_decodebin[WFD_SINK_V_D_H265_DEC]);
3869 case MM_WFD_SINK_VIDEO_CODEC_VP9:
3870 if (v_decodebin[WFD_SINK_V_D_VP9_PARSE].gst)
3871 element_bucket = g_list_append(element_bucket, &v_decodebin[WFD_SINK_V_D_VP9_PARSE]);
3872 if (v_decodebin[WFD_SINK_V_D_VP9_DEC].gst)
3873 element_bucket = g_list_append(element_bucket, &v_decodebin[WFD_SINK_V_D_VP9_DEC]);
3877 wfd_sink_error("video codec is not decied yet. cannot link video decpdebin...");
3882 if (element_bucket == NULL) {
3883 wfd_sink_error("there are no elements to be linked in the video decodebin, destroy it");
3884 if (MM_ERROR_NONE != __mm_wfd_sink_destroy_video_decodebin(wfd_sink)) {
3885 wfd_sink_error("failed to destroy video decodebin");
3891 /* adding elements to video decodebin */
3892 if (!__mm_wfd_sink_gst_element_add_bucket_to_bin(GST_BIN_CAST(v_decodebin[WFD_SINK_V_D_BIN].gst), element_bucket, FALSE)) {
3893 wfd_sink_error("failed to add elements to video decodebin");
3897 /* linking elements in the bucket by added order. */
3898 if (__mm_wfd_sink_gst_element_link_bucket(element_bucket) == -1) {
3899 wfd_sink_error("failed to link elements of the video decodebin");
3903 /* get first element's sinkpad for creating ghostpad */
3904 list_temp = g_list_first(element_bucket);
3905 if (list_temp == NULL) {
3906 wfd_sink_error("failed to get first list of the element_bucket");
3910 first_element = (MMWFDSinkGstElement *)list_temp->data;
3911 if (!first_element) {
3912 wfd_sink_error("failed to get first element of the video decodebin");
3916 sinkpad = gst_element_get_static_pad(GST_ELEMENT(first_element->gst), "sink");
3918 wfd_sink_error("failed to get sink pad from element(%s)",
3919 GST_STR_NULL(GST_ELEMENT_NAME(first_element->gst)));
3923 ghostpad = gst_ghost_pad_new("sink", sinkpad);
3925 wfd_sink_error("failed to create ghostpad of video decodebin");
3929 if (FALSE == gst_element_add_pad(v_decodebin[WFD_SINK_V_D_BIN].gst, ghostpad)) {
3930 wfd_sink_error("failed to add ghostpad to video decodebin");
3933 gst_object_unref(GST_OBJECT(sinkpad));
3937 /* get last element's src for creating ghostpad */
3938 list_temp = g_list_last(element_bucket);
3939 if (list_temp == NULL) {
3940 wfd_sink_error("failed to get last list of the element_bucket");
3944 last_element = (MMWFDSinkGstElement *)list_temp->data;
3945 if (!last_element) {
3946 wfd_sink_error("failed to get last element of the video decodebin");
3950 srcpad = gst_element_get_static_pad(GST_ELEMENT(last_element->gst), "src");
3952 wfd_sink_error("failed to get src pad from element(%s)",
3953 GST_STR_NULL(GST_ELEMENT_NAME(last_element->gst)));
3957 ghostpad = gst_ghost_pad_new("src", srcpad);
3959 wfd_sink_error("failed to create ghostpad of video decodebin");
3963 if (FALSE == gst_element_add_pad(v_decodebin[WFD_SINK_V_D_BIN].gst, ghostpad)) {
3964 wfd_sink_error("failed to add ghostpad to video decodebin");
3967 gst_object_unref(GST_OBJECT(srcpad));
3970 g_list_free(element_bucket);
3973 wfd_sink->video_decodebin_is_linked = TRUE;
3975 wfd_sink_debug_fleave();
3977 return MM_ERROR_NONE;
3981 if (srcpad != NULL) {
3982 gst_object_unref(GST_OBJECT(srcpad));
3986 if (sinkpad != NULL) {
3987 gst_object_unref(GST_OBJECT(sinkpad));
3991 if (element_bucket != NULL) {
3992 g_list_free(element_bucket);
3993 element_bucket = NULL;
3995 return MM_ERROR_WFD_INTERNAL;
3998 static int __mm_wfd_sink_prepare_videodec(mm_wfd_sink_t *wfd_sink, GstElement *video_dec)
4000 wfd_sink_debug_fenter();
4002 /* check video decoder is created */
4003 wfd_sink_return_val_if_fail(video_dec, MM_ERROR_WFD_INVALID_ARGUMENT);
4004 wfd_sink_return_val_if_fail(wfd_sink && wfd_sink->attrs, MM_ERROR_WFD_NOT_INITIALIZED);
4006 wfd_sink_debug_fleave();
4008 return MM_ERROR_NONE;
4011 static int __mm_wfd_sink_prepare_videosink(mm_wfd_sink_t *wfd_sink, GstElement *video_sink)
4013 gboolean visible = TRUE;
4014 gint surface_type = MM_DISPLAY_SURFACE_OVERLAY;
4016 wfd_sink_debug_fenter();
4018 /* check videosink is created */
4019 wfd_sink_return_val_if_fail(video_sink, MM_ERROR_WFD_INVALID_ARGUMENT);
4020 wfd_sink_return_val_if_fail(wfd_sink && wfd_sink->attrs, MM_ERROR_WFD_NOT_INITIALIZED);
4022 /* update display surface */
4023 mm_attrs_get_int_by_name(wfd_sink->attrs, "display_surface_type", &surface_type);
4024 wfd_sink_info("check display surface type attribute: %d", surface_type);
4025 mm_attrs_get_int_by_name(wfd_sink->attrs, "display_visible", &visible);
4026 wfd_sink_info("check display visible attribute: %d", visible);
4028 if (FALSE == visible) {
4029 wfd_sink_info("skipped to prepare video sink. display_visible is FALSE.");
4030 g_object_set(G_OBJECT(video_sink), "visible", visible, NULL);
4031 return MM_ERROR_NONE;
4034 /* configuring display */
4035 switch (surface_type) {
4036 case MM_DISPLAY_SURFACE_EVAS: {
4037 void *object = NULL;
4040 /* common case if using evas surface */
4041 mm_attrs_get_data_by_name(wfd_sink->attrs, "display_overlay", &object);
4042 mm_attrs_get_int_by_name(wfd_sink->attrs, "display_evas_do_scaling", &scaling);
4044 wfd_sink_debug("set video param : evas-object %p", object);
4045 g_object_set(G_OBJECT(video_sink), "evas-object", object, NULL);
4047 wfd_sink_error("no evas object");
4048 return MM_ERROR_WFD_INTERNAL;
4053 case MM_DISPLAY_SURFACE_OVERLAY: {
4054 static unsigned int wl_surface_id = 0;
4055 static void *display_overlay = NULL;
4056 int wl_window_x = 0;
4057 int wl_window_y = 0;
4058 int wl_window_width = 0;
4059 int wl_window_height = 0;
4060 struct wl_surface *wl_surface = NULL;
4061 struct wl_display *wl_display = NULL;
4062 Ecore_Wl2_Window *wl2_window = NULL;
4063 Ecore_Wl2_Display *wl2_display = NULL;
4064 wl_client *wlclient = NULL;
4065 Evas_Object *obj = NULL;
4066 void *object = NULL;
4067 const char *object_type = NULL;
4070 mm_attrs_get_data_by_name(wfd_sink->attrs, "display_overlay", &object);
4072 if (object != NULL) {
4073 obj = (Evas_Object *)object;
4074 object_type = evas_object_type_get(obj);
4075 wfd_sink_debug("window object type : %s", object_type);
4077 /* wayland overlay surface */
4078 LOGI("Wayland overlay surface type");
4079 evas_object_geometry_get(obj, &wl_window_x, &wl_window_y, &wl_window_width, &wl_window_height);
4081 wfd_sink_debug("x[%d] y[%d] width[%d] height[%d]", wl_window_x, wl_window_y,
4082 wl_window_width, wl_window_height);
4084 wl2_window = ecore_evas_wayland2_window_get(ecore_evas_ecore_evas_get(evas_object_evas_get(obj)));
4085 ecore_wl2_window_video_has(wl2_window, EINA_TRUE);
4086 wl_surface = ecore_wl2_window_surface_get(wl2_window);
4088 /* get wl_display */
4089 wl2_display = ecore_wl2_connected_display_get(NULL);
4090 wl_display = ecore_wl2_display_get(wl2_display);
4092 wfd_sink_debug("previous display object : %p current object : %p", display_overlay, object);
4093 if (wl_surface && wl_display && (wl_surface_id == 0 || display_overlay != object)) {
4094 wfd_sink_debug("surface = %p, wl_display = %p", wl_surface, wl_display);
4095 display_overlay = object;
4097 ret = mm_wfd_sink_wlclient_create(&wlclient);
4098 if (ret != MM_ERROR_NONE) {
4099 wfd_sink_error("Wayland client create failure");
4102 wfd_sink_debug("Try to get surface id");
4104 wl_surface_id = mm_wfd_sink_wlclient_get_wl_window_wl_surface_id(wlclient, wl_surface, wl_display);
4106 wfd_sink_debug("wl_surface_id = %d", wl_surface_id);
4113 wfd_sink_debug("set video param : surface_id %d", wl_surface_id);
4115 if (USE_EXTERNAL_WL_DISPLAY_HANDLE) {
4116 gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(video_sink),
4119 gst_video_overlay_set_wl_window_wl_surface_id(GST_VIDEO_OVERLAY(video_sink),
4123 wfd_sink_debug("display object is NULL!");
4124 return MM_ERROR_WFD_INTERNAL;
4129 case MM_DISPLAY_SURFACE_NULL: {
4131 wfd_sink_error("Not Supported Surface.");
4132 return MM_ERROR_WFD_INTERNAL;
4136 wfd_sink_error("Not Supported Surface.(default case)");
4137 return MM_ERROR_WFD_INTERNAL;
4142 g_object_set(G_OBJECT(video_sink), "qos", FALSE, NULL);
4143 g_object_set(G_OBJECT(video_sink), "async", wfd_sink->ini.video_sink_async, NULL);
4144 g_object_set(G_OBJECT(video_sink), "max-lateness", (gint64)wfd_sink->ini.video_sink_max_lateness, NULL);
4145 g_object_set(G_OBJECT(video_sink), "visible", visible, NULL);
4146 g_object_set(G_OBJECT(video_sink), "ts-offset", (gint64)(wfd_sink->ini.sink_ts_offset), NULL);
4147 g_object_set(G_OBJECT(video_sink), "use-tbm", FALSE, NULL);
4149 wfd_sink_debug_fleave();
4151 return MM_ERROR_NONE;
4154 int __mm_wfd_sink_destroy_video_decodebin(mm_wfd_sink_t *wfd_sink)
4156 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
4157 MMWFDSinkGstElement *v_decodebin = NULL;
4158 GstObject *parent = NULL;
4161 wfd_sink_error_fenter();
4163 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
4165 if (wfd_sink->pipeline &&
4166 wfd_sink->pipeline->v_decodebin &&
4167 wfd_sink->pipeline->v_decodebin[WFD_SINK_V_D_BIN].gst) {
4168 v_decodebin = wfd_sink->pipeline->v_decodebin;
4170 wfd_sink_debug("video decodebin is not created, nothing to destroy");
4171 return MM_ERROR_NONE;
4175 parent = gst_element_get_parent(v_decodebin[WFD_SINK_V_D_BIN].gst);
4177 wfd_sink_debug("video decodebin has no parent.. need to relase by itself");
4179 if (GST_STATE(v_decodebin[WFD_SINK_V_D_BIN].gst) >= GST_STATE_READY) {
4180 wfd_sink_error("try to change state of video decodebin to NULL");
4181 ret = gst_element_set_state(v_decodebin[WFD_SINK_V_D_BIN].gst, GST_STATE_NULL);
4182 if (ret != GST_STATE_CHANGE_SUCCESS) {
4183 wfd_sink_error("failed to change state of video decodebin to NULL");
4184 return MM_ERROR_WFD_INTERNAL;
4187 /* release element which are not added to bin */
4188 for (i = 1; i < WFD_SINK_V_D_NUM; i++) { /* NOTE : skip bin */
4189 if (v_decodebin[i].gst) {
4190 parent = gst_element_get_parent(v_decodebin[i].gst);
4192 wfd_sink_debug("unref %s(current ref %d)",
4193 GST_STR_NULL(GST_ELEMENT_NAME(v_decodebin[i].gst)),
4194 ((GObject *) v_decodebin[i].gst)->ref_count);
4195 gst_object_unref(GST_OBJECT(v_decodebin[i].gst));
4196 v_decodebin[i].gst = NULL;
4198 wfd_sink_debug("%s has parent.(current ref %d)",
4199 GST_STR_NULL(GST_ELEMENT_NAME(v_decodebin[i].gst)),
4200 ((GObject *) v_decodebin[i].gst)->ref_count);
4201 gst_object_unref(GST_OBJECT(parent));
4206 /* release video decodebin with it's childs */
4207 if (v_decodebin[WFD_SINK_V_D_BIN].gst) {
4208 wfd_sink_debug("unref %s(current ref %d)",
4209 GST_STR_NULL(GST_ELEMENT_NAME(v_decodebin[WFD_SINK_V_D_BIN].gst)),
4210 ((GObject *)v_decodebin[WFD_SINK_V_D_BIN].gst)->ref_count);
4212 gst_object_unref(GST_OBJECT(v_decodebin[WFD_SINK_V_D_BIN].gst));
4213 v_decodebin[WFD_SINK_V_D_BIN].gst = NULL;
4216 wfd_sink_debug("video decodebin has parent(%s), unref it",
4217 GST_STR_NULL(GST_OBJECT_NAME(GST_OBJECT(parent))));
4219 gst_object_unref(GST_OBJECT(parent));
4223 wfd_sink->video_decodebin_is_linked = FALSE;
4225 MMWFDSINK_FREEIF(wfd_sink->pipeline->v_decodebin);
4227 wfd_sink_error_fleave();
4229 return MM_ERROR_NONE;
4232 int __mm_wfd_sink_create_video_decodebin(mm_wfd_sink_t *wfd_sink)
4234 MMWFDSinkGstElement *v_decodebin = NULL;
4235 guint video_codec = WFD_VIDEO_UNKNOWN;
4236 GList *element_bucket = NULL;
4237 gboolean link = TRUE;
4240 wfd_sink_debug_fenter();
4242 wfd_sink_return_val_if_fail(wfd_sink &&
4244 MM_ERROR_WFD_NOT_INITIALIZED);
4246 if (wfd_sink->pipeline->v_decodebin) {
4247 wfd_sink_debug("video decodebin is already created... nothing to do");
4248 return MM_ERROR_NONE;
4251 if (wfd_sink->stream_info.video_stream_info.codec == MM_WFD_SINK_VIDEO_CODEC_NONE) {
4252 wfd_sink_debug("Skip create video decodebin for none video codec.");
4253 wfd_sink_debug_fleave();
4254 return MM_ERROR_NONE;
4257 /* check video decodebin could be linked now */
4258 switch (wfd_sink->stream_info.video_stream_info.codec) {
4259 case MM_WFD_SINK_VIDEO_CODEC_H264:
4260 video_codec = WFD_VIDEO_H264;
4263 case MM_WFD_SINK_VIDEO_CODEC_H265:
4264 video_codec = WFD_VIDEO_H265;
4267 case MM_WFD_SINK_VIDEO_CODEC_VP9:
4268 video_codec = WFD_VIDEO_VP9;
4271 case MM_WFD_SINK_VIDEO_CODEC_NONE:
4273 wfd_sink_debug("video decodebin could NOT be linked now, just create");
4274 video_codec = wfd_sink->ini.wfd_video_formats.video_codec | wfd_sink->ini.wfd2_video_formats.video_codec;
4280 v_decodebin = (MMWFDSinkGstElement *)g_malloc0(sizeof(MMWFDSinkGstElement) * WFD_SINK_V_D_NUM);
4282 wfd_sink_error("failed to allocate memory for video decodebin");
4283 return MM_ERROR_WFD_NO_FREE_SPACE;
4286 memset(v_decodebin, 0, sizeof(MMWFDSinkGstElement) * WFD_SINK_V_D_NUM);
4288 /* create video decodebin */
4289 v_decodebin[WFD_SINK_V_D_BIN].id = WFD_SINK_V_D_BIN;
4290 v_decodebin[WFD_SINK_V_D_BIN].gst = gst_bin_new("video_decodebin");
4291 if (!v_decodebin[WFD_SINK_V_D_BIN].gst) {
4292 wfd_sink_error("failed to create video decodebin");
4297 MMWFDSINK_CREATE_ELEMENT(v_decodebin, WFD_SINK_V_D_QUEUE, "queue", "video_queue", FALSE);
4298 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_QUEUE].gst, "sink");
4299 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_QUEUE].gst, "src");
4300 if (v_decodebin[WFD_SINK_V_D_QUEUE].gst)
4301 __mm_wfd_sink_prepare_queue(wfd_sink, v_decodebin[WFD_SINK_V_D_QUEUE].gst);
4304 MMWFDSINK_CREATE_ELEMENT(v_decodebin, WFD_SINK_V_D_HDCP, wfd_sink->ini.name_of_video_hdcp, "video_hdcp", FALSE);
4305 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_HDCP].gst, "sink");
4306 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_HDCP].gst, "src");
4308 if (video_codec & WFD_VIDEO_H264) {
4310 MMWFDSINK_CREATE_ELEMENT(v_decodebin, WFD_SINK_V_D_H264_PARSE, wfd_sink->ini.name_of_video_h264_parser, "video_h264_parser", FALSE);
4311 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_H264_PARSE].gst, "sink");
4312 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_H264_PARSE].gst, "src");
4315 MMWFDSINK_CREATE_ELEMENT(v_decodebin, WFD_SINK_V_D_H264_DEC, wfd_sink->ini.name_of_video_h264_decoder, "video_h264_dec", FALSE);
4316 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_H264_DEC].gst, "sink");
4317 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_H264_DEC].gst, "src");
4318 if (v_decodebin[WFD_SINK_V_D_H264_DEC].gst) {
4319 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_videodec(wfd_sink, v_decodebin[WFD_SINK_V_D_H264_DEC].gst)) {
4320 wfd_sink_error("failed to set video decoder property...");
4326 if (video_codec & WFD_VIDEO_H265) {
4328 MMWFDSINK_CREATE_ELEMENT(v_decodebin, WFD_SINK_V_D_H265_PARSE, wfd_sink->ini.name_of_video_h264_parser, "video_h265_parser", FALSE);
4329 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_H265_PARSE].gst, "sink");
4330 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_H265_PARSE].gst, "src");
4333 MMWFDSINK_CREATE_ELEMENT(v_decodebin, WFD_SINK_V_D_H265_DEC, wfd_sink->ini.name_of_video_h265_decoder, "video_h265_dec", FALSE);
4334 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_H265_DEC].gst, "sink");
4335 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_H265_DEC].gst, "src");
4336 if (v_decodebin[WFD_SINK_V_D_H265_DEC].gst) {
4337 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_videodec(wfd_sink, v_decodebin[WFD_SINK_V_D_H265_DEC].gst)) {
4338 wfd_sink_error("failed to set video decoder property...");
4343 if (video_codec & WFD_VIDEO_VP9) {
4345 MMWFDSINK_CREATE_ELEMENT(v_decodebin, WFD_SINK_V_D_VP9_PARSE, wfd_sink->ini.name_of_video_vp9_parser, "video_vp9_parser", FALSE);
4346 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_VP9_PARSE].gst, "sink");
4347 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_VP9_PARSE].gst, "src");
4350 MMWFDSINK_CREATE_ELEMENT(v_decodebin, WFD_SINK_V_D_VP9_DEC, wfd_sink->ini.name_of_video_vp9_decoder, "video_vp9_dec", FALSE);
4351 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_VP9_DEC].gst, "sink");
4352 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_decodebin[WFD_SINK_V_D_VP9_DEC].gst, "src");
4353 if (v_decodebin[WFD_SINK_V_D_VP9_DEC].gst) {
4354 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_videodec(wfd_sink, v_decodebin[WFD_SINK_V_D_VP9_DEC].gst)) {
4355 wfd_sink_error("failed to set video decoder property...");
4361 g_list_free(element_bucket);
4364 wfd_sink->pipeline->v_decodebin = v_decodebin;
4366 /* link video decodebin if video codec is fixed */
4368 if (MM_ERROR_NONE != __mm_wfd_sink_link_video_decodebin(wfd_sink)) {
4369 wfd_sink_error("failed to link video decodebin, destroy video decodebin");
4370 __mm_wfd_sink_destroy_video_decodebin(wfd_sink);
4371 return MM_ERROR_WFD_INTERNAL;
4375 wfd_sink_debug_fleave();
4377 return MM_ERROR_NONE;
4381 wfd_sink_error("failed to create video decodebin, releasing all");
4383 g_list_free(element_bucket);
4385 /* release element which are not added to bin */
4386 for (i = 1; i < WFD_SINK_V_D_NUM; i++) { /* NOTE : skip bin */
4387 if (v_decodebin != NULL && v_decodebin[i].gst) {
4388 GstObject *parent = NULL;
4389 parent = gst_element_get_parent(v_decodebin[i].gst);
4392 gst_object_unref(GST_OBJECT(v_decodebin[i].gst));
4393 v_decodebin[i].gst = NULL;
4395 gst_object_unref(GST_OBJECT(parent));
4401 /* release video decodebin with it's childs */
4402 if (v_decodebin != NULL && v_decodebin[WFD_SINK_V_D_BIN].gst) {
4403 gst_object_unref(GST_OBJECT(v_decodebin[WFD_SINK_V_D_BIN].gst));
4404 v_decodebin[WFD_SINK_V_D_BIN].gst = NULL;
4407 MMWFDSINK_FREEIF(v_decodebin);
4409 return MM_ERROR_WFD_INTERNAL;
4412 int __mm_wfd_sink_destroy_video_sinkbin(mm_wfd_sink_t *wfd_sink)
4414 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
4415 MMWFDSinkGstElement *v_sinkbin = NULL;
4416 GstObject *parent = NULL;
4419 wfd_sink_error_fenter();
4421 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
4423 if (wfd_sink->pipeline &&
4424 wfd_sink->pipeline->v_sinkbin &&
4425 wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_BIN].gst) {
4426 v_sinkbin = wfd_sink->pipeline->v_sinkbin;
4428 wfd_sink_debug("video sinkbin is not created, nothing to destroy");
4429 return MM_ERROR_NONE;
4433 parent = gst_element_get_parent(v_sinkbin[WFD_SINK_V_S_BIN].gst);
4435 wfd_sink_debug("video sinkbin has no parent.. need to relase by itself");
4437 if (GST_STATE(v_sinkbin[WFD_SINK_V_S_BIN].gst) >= GST_STATE_READY) {
4438 wfd_sink_error("try to change state of video sinkbin to NULL");
4439 ret = gst_element_set_state(v_sinkbin[WFD_SINK_V_S_BIN].gst, GST_STATE_NULL);
4440 if (ret != GST_STATE_CHANGE_SUCCESS) {
4441 wfd_sink_error("failed to change state of video sinkbin to NULL");
4442 return MM_ERROR_WFD_INTERNAL;
4445 /* release element which are not added to bin */
4446 for (i = 1; i < WFD_SINK_V_S_NUM; i++) { /* NOTE : skip bin */
4447 if (v_sinkbin[i].gst) {
4448 parent = gst_element_get_parent(v_sinkbin[i].gst);
4450 wfd_sink_debug("unref %s(current ref %d)",
4451 GST_STR_NULL(GST_ELEMENT_NAME(v_sinkbin[i].gst)),
4452 ((GObject *) v_sinkbin[i].gst)->ref_count);
4453 gst_object_unref(GST_OBJECT(v_sinkbin[i].gst));
4454 v_sinkbin[i].gst = NULL;
4456 wfd_sink_debug("%s has parent.(current ref %d)",
4457 GST_STR_NULL(GST_ELEMENT_NAME(v_sinkbin[i].gst)),
4458 ((GObject *) v_sinkbin[i].gst)->ref_count);
4459 gst_object_unref(GST_OBJECT(parent));
4464 /* release video sinkbin with it's childs */
4465 if (v_sinkbin[WFD_SINK_V_S_BIN].gst) {
4466 gst_object_unref(GST_OBJECT(v_sinkbin[WFD_SINK_V_S_BIN].gst));
4467 v_sinkbin[WFD_SINK_V_S_BIN].gst = NULL;
4470 wfd_sink_debug("video sinkbin has parent(%s), unref it ",
4471 GST_STR_NULL(GST_OBJECT_NAME(GST_OBJECT(parent))));
4473 gst_object_unref(GST_OBJECT(parent));
4477 MMWFDSINK_FREEIF(wfd_sink->pipeline->v_sinkbin);
4479 wfd_sink_error_fleave();
4481 return MM_ERROR_NONE;
4484 int __mm_wfd_sink_create_video_sinkbin(mm_wfd_sink_t *wfd_sink)
4486 MMWFDSinkGstElement *first_element = NULL;
4487 MMWFDSinkGstElement *v_sinkbin = NULL;
4488 GList *element_bucket = NULL;
4490 GstPad *ghostpad = NULL;
4492 gint surface_type = MM_DISPLAY_SURFACE_OVERLAY;
4494 wfd_sink_debug_fenter();
4496 wfd_sink_return_val_if_fail(wfd_sink &&
4498 MM_ERROR_WFD_NOT_INITIALIZED);
4500 if (wfd_sink->pipeline->v_sinkbin != NULL) {
4501 wfd_sink_error("The video sink bin is already created.");
4502 return MM_ERROR_NONE;
4505 if (wfd_sink->stream_info.video_stream_info.codec == MM_WFD_SINK_VIDEO_CODEC_NONE) {
4506 wfd_sink_error("Skip create video sink bin for non video codec.");
4507 wfd_sink_debug_fleave();
4508 return MM_ERROR_NONE;
4512 v_sinkbin = (MMWFDSinkGstElement *)g_malloc0(sizeof(MMWFDSinkGstElement) * WFD_SINK_V_S_NUM);
4514 wfd_sink_error("failed to allocate memory for video sinkbin");
4515 return MM_ERROR_WFD_NO_FREE_SPACE;
4518 memset(v_sinkbin, 0, sizeof(MMWFDSinkGstElement) * WFD_SINK_V_S_NUM);
4520 /* create video sinkbin */
4521 v_sinkbin[WFD_SINK_V_S_BIN].id = WFD_SINK_V_S_BIN;
4522 v_sinkbin[WFD_SINK_V_S_BIN].gst = gst_bin_new("video_sinkbin");
4523 if (!v_sinkbin[WFD_SINK_V_S_BIN].gst) {
4524 wfd_sink_error("failed to create video sinkbin");
4528 /* create convert */
4529 MMWFDSINK_CREATE_ELEMENT(v_sinkbin, WFD_SINK_V_S_CONVERT, wfd_sink->ini.name_of_video_converter, "video_convert", TRUE);
4530 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_sinkbin[WFD_SINK_V_S_CONVERT].gst, "sink");
4531 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_sinkbin[WFD_SINK_V_S_CONVERT].gst, "src");
4534 MMWFDSINK_CREATE_ELEMENT(v_sinkbin, WFD_SINK_V_S_FILTER, wfd_sink->ini.name_of_video_filter, "video_filter", TRUE);
4535 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_sinkbin[WFD_SINK_V_S_FILTER].gst, "sink");
4536 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_sinkbin[WFD_SINK_V_S_FILTER].gst, "src");
4537 if (v_sinkbin[WFD_SINK_V_S_FILTER].gst) {
4538 GstCaps *caps = NULL;
4539 caps = gst_caps_new_simple("video/x-raw", "format", G_TYPE_STRING, "SN12", NULL);
4540 g_object_set(G_OBJECT(v_sinkbin[WFD_SINK_V_S_FILTER].gst), "caps", caps, NULL);
4541 gst_object_unref(GST_OBJECT(caps));
4546 mm_attrs_get_int_by_name(wfd_sink->attrs, "display_surface_type", &surface_type);
4548 if (surface_type == MM_DISPLAY_SURFACE_OVERLAY) {
4549 MMWFDSINK_CREATE_ELEMENT(v_sinkbin, WFD_SINK_V_S_SINK, wfd_sink->ini.name_of_video_sink, "video_sink", TRUE);
4550 } else if (surface_type == MM_DISPLAY_SURFACE_EVAS) {
4551 MMWFDSINK_CREATE_ELEMENT(v_sinkbin, WFD_SINK_V_S_SINK, wfd_sink->ini.name_of_video_evas_sink, "video_sink", TRUE);
4553 wfd_sink_error("failed to set video sink....");
4557 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, v_sinkbin[WFD_SINK_V_S_SINK].gst, "sink");
4558 if (v_sinkbin[WFD_SINK_V_S_SINK].gst) {
4559 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_videosink(wfd_sink, v_sinkbin[WFD_SINK_V_S_SINK].gst)) {
4560 wfd_sink_error("failed to set video sink property....");
4565 /* adding created elements to video sinkbin */
4566 if (!__mm_wfd_sink_gst_element_add_bucket_to_bin(GST_BIN_CAST(v_sinkbin[WFD_SINK_V_S_BIN].gst), element_bucket, FALSE)) {
4567 wfd_sink_error("failed to add elements to video sinkbin");
4571 /* linking elements in the bucket by added order. */
4572 if (__mm_wfd_sink_gst_element_link_bucket(element_bucket) == -1) {
4573 wfd_sink_error("failed to link elements of the video sinkbin");
4577 /* get first element's sinkpad for creating ghostpad */
4578 first_element = (MMWFDSinkGstElement *)g_list_nth_data(element_bucket, 0);
4579 if (!first_element) {
4580 wfd_sink_error("failed to get first element of the video sinkbin");
4584 pad = gst_element_get_static_pad(GST_ELEMENT(first_element->gst), "sink");
4586 wfd_sink_error("failed to get pad from first element(%s) of the video sinkbin",
4587 GST_STR_NULL(GST_ELEMENT_NAME(first_element->gst)));
4591 ghostpad = gst_ghost_pad_new("sink", pad);
4593 wfd_sink_error("failed to create ghostpad of the video sinkbin");
4597 if (FALSE == gst_element_add_pad(v_sinkbin[WFD_SINK_V_S_BIN].gst, ghostpad)) {
4598 wfd_sink_error("failed to add ghostpad to video sinkbin");
4602 gst_object_unref(GST_OBJECT(pad));
4605 g_list_free(element_bucket);
4606 element_bucket = NULL;
4609 wfd_sink->pipeline->v_sinkbin = v_sinkbin;
4611 wfd_sink_debug_fleave();
4613 return MM_ERROR_NONE;
4617 wfd_sink_error("failed to create video sinkbin, releasing all");
4620 gst_object_unref(GST_OBJECT(pad));
4625 gst_object_unref(GST_OBJECT(ghostpad));
4629 if (element_bucket) {
4630 g_list_free(element_bucket);
4631 element_bucket = NULL;
4634 /* release element which are not added to bin */
4635 for (i = 1; i < WFD_SINK_V_S_NUM; i++) { /* NOTE : skip bin */
4636 if (v_sinkbin != NULL && v_sinkbin[i].gst) {
4637 GstObject *parent = NULL;
4638 parent = gst_element_get_parent(v_sinkbin[i].gst);
4641 gst_object_unref(GST_OBJECT(v_sinkbin[i].gst));
4642 v_sinkbin[i].gst = NULL;
4644 gst_object_unref(GST_OBJECT(parent));
4650 /* release video sinkbin with it's childs */
4651 if (v_sinkbin != NULL && v_sinkbin[WFD_SINK_V_S_BIN].gst) {
4652 gst_object_unref(GST_OBJECT(v_sinkbin[WFD_SINK_V_S_BIN].gst));
4653 v_sinkbin[WFD_SINK_V_S_BIN].gst = NULL;
4655 MMWFDSINK_FREEIF(v_sinkbin);
4657 return MM_ERROR_WFD_INTERNAL;
4660 int __mm_wfd_sink_destroy_pipeline(mm_wfd_sink_t *wfd_sink)
4662 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
4665 wfd_sink_debug_fenter();
4667 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
4669 /* cleanup gst stuffs */
4670 if (wfd_sink->pipeline) {
4671 MMWFDSinkGstElement *mainbin = wfd_sink->pipeline->mainbin;
4675 PRINT_WFD_REF_COUNT(wfd_sink);
4677 ret = gst_element_set_state(mainbin[WFD_SINK_M_PIPE].gst, GST_STATE_NULL);
4678 if (ret != GST_STATE_CHANGE_SUCCESS) {
4679 wfd_sink_error("failed to change state of pipeline to NULL. ret[%d]", ret);
4680 return MM_ERROR_WFD_INTERNAL;
4682 wfd_sink_debug("Successed to change state of pipeline to NULL");
4685 bus = gst_pipeline_get_bus(GST_PIPELINE(mainbin[WFD_SINK_M_PIPE].gst));
4687 GstMessage *gst_msg = NULL;
4688 while ((gst_msg = gst_bus_pop(bus)) != NULL) {
4689 _mm_wfd_sink_msg_callback(bus, gst_msg, (gpointer)wfd_sink);
4690 gst_message_unref(gst_msg);
4693 gst_object_unref(bus);
4697 PRINT_WFD_REF_COUNT(wfd_sink);
4699 if (MM_ERROR_NONE != __mm_wfd_sink_destroy_video_decodebin(wfd_sink)) {
4700 wfd_sink_error("failed to destroy video decodebin");
4701 return MM_ERROR_WFD_INTERNAL;
4704 if (MM_ERROR_NONE != __mm_wfd_sink_destroy_audio_decodebin(wfd_sink)) {
4705 wfd_sink_error("failed to destroy audio decodebin");
4706 return MM_ERROR_WFD_INTERNAL;
4709 if (MM_ERROR_NONE != __mm_wfd_sink_destroy_video_sinkbin(wfd_sink)) {
4710 wfd_sink_error("failed to destroy video sinkbin");
4711 return MM_ERROR_WFD_INTERNAL;
4714 if (MM_ERROR_NONE != __mm_wfd_sink_destroy_audio_sinkbin(wfd_sink)) {
4715 wfd_sink_error("failed to destroy audio sinkbin");
4716 return MM_ERROR_WFD_INTERNAL;
4719 gst_object_unref(GST_OBJECT(mainbin[WFD_SINK_M_PIPE].gst));
4720 mainbin[WFD_SINK_M_PIPE].gst = NULL;
4722 MMWFDSINK_FREEIF(mainbin);
4725 MMWFDSINK_FREEIF(wfd_sink->pipeline);
4728 if (wfd_sink->msg_callback_id > 0) {
4729 g_source_remove(wfd_sink->msg_callback_id);
4730 wfd_sink->msg_callback_id = 0;
4733 wfd_sink->audio_decodebin_is_linked = FALSE;
4734 wfd_sink->video_decodebin_is_linked = FALSE;
4735 wfd_sink->need_to_reset_basetime = FALSE;
4736 wfd_sink->supportive_resolution = MM_WFD_SINK_RESOLUTION_UNKNOWN;
4738 wfd_sink_debug_fleave();
4740 return MM_ERROR_NONE;
4744 __mm_wfd_sink_dump_pipeline_state(mm_wfd_sink_t *wfd_sink)
4746 GstIterator *iter = NULL;
4747 gboolean done = FALSE;
4749 GstElement *item = NULL;
4750 GstElementFactory *factory = NULL;
4752 GstState state = GST_STATE_VOID_PENDING;
4753 GstState pending = GST_STATE_VOID_PENDING;
4754 GstClockTime time = 200 * GST_MSECOND;
4756 wfd_sink_debug_fenter();
4758 wfd_sink_return_if_fail(wfd_sink &&
4759 wfd_sink->pipeline &&
4760 wfd_sink->pipeline->mainbin &&
4761 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst);
4763 iter = gst_bin_iterate_recurse(GST_BIN(wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst));
4767 switch (gst_iterator_next(iter, (gpointer)&item)) {
4768 case GST_ITERATOR_OK:
4769 gst_element_get_state(GST_ELEMENT(item), &state, &pending, time);
4771 factory = gst_element_get_factory(item) ;
4773 wfd_sink_error("%s:%s : From:%s To:%s refcount : %d",
4774 GST_STR_NULL(GST_OBJECT_NAME(factory)),
4775 GST_STR_NULL(GST_ELEMENT_NAME(item)),
4776 gst_element_state_get_name(state),
4777 gst_element_state_get_name(pending),
4778 GST_OBJECT_REFCOUNT_VALUE(item));
4780 gst_object_unref(item);
4783 case GST_ITERATOR_RESYNC:
4784 gst_iterator_resync(iter);
4786 case GST_ITERATOR_ERROR:
4789 case GST_ITERATOR_DONE:
4799 item = GST_ELEMENT_CAST(wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst);
4801 gst_element_get_state(GST_ELEMENT(item), &state, &pending, time);
4803 factory = gst_element_get_factory(item) ;
4805 wfd_sink_error("%s:%s : From:%s To:%s refcount : %d",
4806 GST_OBJECT_NAME(factory),
4807 GST_ELEMENT_NAME(item),
4808 gst_element_state_get_name(state),
4809 gst_element_state_get_name(pending),
4810 GST_OBJECT_REFCOUNT_VALUE(item));
4814 gst_iterator_free(iter);
4816 wfd_sink_debug_fleave();
4822 _mm_wfds_sink_get_state_name(MMWFDSinkStateType state)
4825 case MM_WFD_SINK_STATE_NONE:
4827 case MM_WFD_SINK_STATE_NULL:
4829 case MM_WFD_SINK_STATE_PREPARED:
4831 case MM_WFD_SINK_STATE_CONNECTED:
4833 case MM_WFD_SINK_STATE_PLAYING:
4835 case MM_WFD_SINK_STATE_PAUSED:
4837 case MM_WFD_SINK_STATE_DISCONNECTED:
4838 return "DISCONNECTED";
4844 int _mm_wfd_sink_set_resolution(mm_wfd_sink_t *wfd_sink, MMWFDSinkResolution resolution)
4846 MMWFDSinkStateType cur_state = MM_WFD_SINK_STATE_NONE;
4848 wfd_sink_debug_fenter();
4850 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
4852 MMWFDSINK_PRINT_STATE(wfd_sink);
4853 cur_state = MMWFDSINK_CURRENT_STATE(wfd_sink);
4854 if (cur_state != MM_WFD_SINK_STATE_NULL) {
4855 wfd_sink_error("This function must be called when MM_WFD_SINK_STATE_NULL");
4856 return MM_ERROR_WFD_INVALID_STATE;
4859 wfd_sink->supportive_resolution = resolution;
4861 wfd_sink_debug_fleave();
4863 return MM_ERROR_NONE;
4866 void __mm_wfd_sink_print_ref_count(mm_wfd_sink_t *wfd_sink)
4869 MMWFDSinkGstElement *mainbin = NULL;
4870 MMWFDSinkGstElement *v_decodebin = NULL;
4871 MMWFDSinkGstElement *a_decodebin = NULL;
4872 MMWFDSinkGstElement *v_sinkbin = NULL;
4873 MMWFDSinkGstElement *a_sinkbin = NULL;
4875 wfd_sink_debug_fenter();
4877 wfd_sink_return_if_fail(wfd_sink);
4878 wfd_sink_return_if_fail(wfd_sink->pipeline);
4880 wfd_sink_debug("************* wfd pipeline ref count start *************");
4881 wfd_sink_debug("try to check mainbin");
4883 if (wfd_sink->pipeline->mainbin &&
4884 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst) {
4885 mainbin = wfd_sink->pipeline->mainbin;
4887 for (i = 0; i < WFD_SINK_M_NUM; i++) {
4888 if (mainbin[i].gst) {
4889 wfd_sink_debug("%s(current ref %d)",
4890 GST_ELEMENT_NAME(mainbin[i].gst),
4891 GST_OBJECT_REFCOUNT(mainbin[i].gst));
4896 wfd_sink_debug("try to check a_decodebin");
4898 if (wfd_sink->pipeline->a_decodebin &&
4899 wfd_sink->pipeline->a_decodebin[WFD_SINK_A_D_BIN].gst) {
4900 a_decodebin = wfd_sink->pipeline->a_decodebin;
4902 for (i = 0; i < WFD_SINK_A_D_NUM; i++) {
4903 if (a_decodebin[i].gst) {
4904 wfd_sink_debug("%s(current ref %d)",
4905 GST_ELEMENT_NAME(a_decodebin[i].gst),
4906 GST_OBJECT_REFCOUNT(a_decodebin[i].gst));
4911 wfd_sink_debug("try to check a_sinkbin");
4913 if (wfd_sink->pipeline->a_sinkbin &&
4914 wfd_sink->pipeline->a_sinkbin[WFD_SINK_A_S_BIN].gst) {
4915 a_sinkbin = wfd_sink->pipeline->a_sinkbin;
4917 for (i = 0; i < WFD_SINK_A_S_NUM; i++) {
4918 if (a_sinkbin[i].gst) {
4919 wfd_sink_debug("%s(current ref %d)",
4920 GST_ELEMENT_NAME(a_sinkbin[i].gst),
4921 GST_OBJECT_REFCOUNT(a_sinkbin[i].gst));
4926 wfd_sink_debug("try to check v_decodebin");
4928 if (wfd_sink->pipeline->v_decodebin &&
4929 wfd_sink->pipeline->v_decodebin[WFD_SINK_V_D_BIN].gst) {
4930 v_decodebin = wfd_sink->pipeline->v_decodebin;
4932 for (i = 0; i < WFD_SINK_V_D_NUM; i++) {
4933 if (v_decodebin[i].gst) {
4934 wfd_sink_debug("%s(current ref %d)",
4935 GST_ELEMENT_NAME(v_decodebin[i].gst),
4936 GST_OBJECT_REFCOUNT(v_decodebin[i].gst));
4941 wfd_sink_debug("try to check v_sinkbin");
4943 if (wfd_sink->pipeline->v_sinkbin &&
4944 wfd_sink->pipeline->v_sinkbin[WFD_SINK_V_S_BIN].gst) {
4945 v_sinkbin = wfd_sink->pipeline->v_sinkbin;
4947 for (i = 0; i < WFD_SINK_V_S_NUM; i++) {
4948 if (v_sinkbin[i].gst) {
4949 wfd_sink_debug("%s(current ref %d)",
4950 GST_ELEMENT_NAME(v_sinkbin[i].gst),
4951 GST_OBJECT_REFCOUNT(v_sinkbin[i].gst));
4955 wfd_sink_debug("************* wfd pipeline ref count end *************");
4956 wfd_sink_debug_fleave();