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>
26 #include "mm_wfd_sink_util.h"
27 #include "mm_wfd_sink_priv.h"
28 #include "mm_wfd_sink_manager.h"
29 #include "mm_wfd_sink_dlog.h"
30 #include <wfdconfigmessage.h>
34 static int __mm_wfd_sink_init_gstreamer(mm_wfd_sink_t *wfd_sink);
35 static int __mm_wfd_sink_create_pipeline(mm_wfd_sink_t *wfd_sink);
36 static int __mm_wfd_sink_create_videobin(mm_wfd_sink_t *wfd_sink);
37 static int __mm_wfd_sink_create_audiobin(mm_wfd_sink_t *wfd_sink);
38 static int __mm_wfd_sink_destroy_pipeline(mm_wfd_sink_t *wfd_sink);
39 static int __mm_wfd_sink_set_pipeline_state(mm_wfd_sink_t *wfd_sink, GstState state, gboolean async);
42 static int __mm_wfd_sink_check_state(mm_wfd_sink_t *wfd_sink, MMWFDSinkCommandType cmd);
43 static int __mm_wfd_sink_set_state(mm_wfd_sink_t *wfd_sink, MMWFDSinkStateType state);
46 static void __mm_wfd_sink_dump_pipeline_state(mm_wfd_sink_t *wfd_sink);
47 static void __mm_wfd_sink_prepare_video_resolution(gint resolution, guint *CEA_resolution,
48 guint *VESA_resolution, guint *HH_resolution);
50 int _mm_wfd_sink_create(mm_wfd_sink_t **wfd_sink)
52 int result = MM_ERROR_NONE;
54 wfd_sink_debug_fenter();
56 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
58 mm_wfd_sink_t *new_wfd_sink = NULL;
61 new_wfd_sink = g_malloc0(sizeof(mm_wfd_sink_t));
63 wfd_sink_error("failed to allocate memory for wi-fi display sink\n");
64 return MM_ERROR_WFD_NO_FREE_SPACE;
67 /* Initialize gstreamer related */
68 new_wfd_sink->attrs = 0;
70 new_wfd_sink->pipeline = NULL;
71 new_wfd_sink->added_av_pad_num = 0;
72 new_wfd_sink->audio_bin_is_linked = FALSE;
73 new_wfd_sink->video_bin_is_linked = FALSE;
74 new_wfd_sink->prev_audio_dec_src_pad = NULL;
75 new_wfd_sink->next_audio_dec_sink_pad = NULL;
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 resource related */
108 new_wfd_sink->resource_list = NULL;
110 /* Initialize manager related */
111 new_wfd_sink->manager_thread = NULL;
112 new_wfd_sink->manager_thread_cmd = WFD_SINK_MANAGER_CMD_NONE;
114 /* Initialize video resolution */
115 new_wfd_sink->supportive_resolution = MM_WFD_SINK_RESOLUTION_UNKNOWN;
117 /* construct attributes */
118 new_wfd_sink->attrs = _mmwfd_construct_attribute((MMHandleType)new_wfd_sink);
119 if (!new_wfd_sink->attrs) {
120 MMWFDSINK_FREEIF(new_wfd_sink);
121 wfd_sink_error("failed to set attribute\n");
122 return MM_ERROR_WFD_INTERNAL;
125 /* load ini for initialize */
126 result = mm_wfd_sink_ini_load(&new_wfd_sink->ini);
127 if (result != MM_ERROR_NONE) {
128 wfd_sink_error("failed to load ini file\n");
129 goto fail_to_load_ini;
131 new_wfd_sink->need_to_reset_basetime = new_wfd_sink->ini.enable_reset_basetime;
133 /* initialize manager */
134 result = _mm_wfd_sink_init_manager(new_wfd_sink);
135 if (result < MM_ERROR_NONE) {
136 wfd_sink_error("failed to init manager : %d\n", result);
140 /* initialize gstreamer */
141 result = __mm_wfd_sink_init_gstreamer(new_wfd_sink);
142 if (result < MM_ERROR_NONE) {
143 wfd_sink_error("failed to init gstreamer : %d\n", result);
148 __mm_wfd_sink_set_state(new_wfd_sink, MM_WFD_SINK_STATE_NULL);
150 /* now take handle */
151 *wfd_sink = new_wfd_sink;
153 wfd_sink_debug_fleave();
159 mm_wfd_sink_ini_unload(&new_wfd_sink->ini);
161 _mmwfd_deconstruct_attribute(new_wfd_sink->attrs);
162 MMWFDSINK_FREEIF(new_wfd_sink);
169 int _mm_wfd_sink_prepare(mm_wfd_sink_t *wfd_sink)
171 int result = MM_ERROR_NONE;
173 wfd_sink_debug_fenter();
175 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
177 /* check current wi-fi display sink state */
178 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_PREPARE);
180 /* construct pipeline */
181 /* create main pipeline */
182 result = __mm_wfd_sink_create_pipeline(wfd_sink);
183 if (result < MM_ERROR_NONE) {
184 wfd_sink_error("failed to create pipeline : %d\n", result);
188 /* create videobin */
189 result = __mm_wfd_sink_create_videobin(wfd_sink);
190 if (result < MM_ERROR_NONE) {
191 wfd_sink_error("failed to create videobin : %d\n", result);
195 /* create audiobin */
196 result = __mm_wfd_sink_create_audiobin(wfd_sink);
197 if (result < MM_ERROR_NONE) {
198 wfd_sink_error("fail to create audiobin : %d\n", result);
202 /* set pipeline READY state */
203 result = __mm_wfd_sink_set_pipeline_state(wfd_sink, GST_STATE_READY, TRUE);
204 if (result < MM_ERROR_NONE) {
205 wfd_sink_error("failed to set state : %d\n", result);
210 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_PREPARED);
212 wfd_sink_debug_fleave();
218 /* need to destroy pipeline already created */
219 __mm_wfd_sink_destroy_pipeline(wfd_sink);
223 int _mm_wfd_sink_connect(mm_wfd_sink_t *wfd_sink, const char *uri)
225 int result = MM_ERROR_NONE;
227 wfd_sink_debug_fenter();
229 wfd_sink_return_val_if_fail(uri && strlen(uri) > strlen("rtsp://"),
230 MM_ERROR_WFD_INVALID_ARGUMENT);
231 wfd_sink_return_val_if_fail(wfd_sink &&
232 wfd_sink->pipeline &&
233 wfd_sink->pipeline->mainbin &&
234 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst &&
235 wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst &&
236 wfd_sink->pipeline->mainbin[WFD_SINK_M_DEPAY].gst &&
237 wfd_sink->pipeline->mainbin[WFD_SINK_M_DEMUX].gst,
238 MM_ERROR_WFD_NOT_INITIALIZED);
240 /* check current wi-fi display sink state */
241 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_CONNECT);
243 wfd_sink_debug("try to connect to %s.....\n", GST_STR_NULL(uri));
245 /* set uri to wfdrtspsrc */
246 g_object_set(G_OBJECT(wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst), "location", uri, NULL);
248 /* set pipeline PAUSED state */
249 result = __mm_wfd_sink_set_pipeline_state(wfd_sink, GST_STATE_PAUSED, TRUE);
250 if (result < MM_ERROR_NONE) {
251 wfd_sink_error("failed to set state : %d\n", result);
255 wfd_sink_debug_fleave();
260 int _mm_wfd_sink_start(mm_wfd_sink_t *wfd_sink)
262 int result = MM_ERROR_NONE;
264 wfd_sink_debug_fenter();
266 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
268 /* check current wi-fi display sink state */
269 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_START);
271 WFD_SINK_MANAGER_LOCK(wfd_sink) ;
272 wfd_sink_debug("check pipeline is ready to start");
273 WFD_SINK_MANAGER_UNLOCK(wfd_sink);
275 result = __mm_wfd_sink_set_pipeline_state(wfd_sink, GST_STATE_PLAYING, TRUE);
276 if (result < MM_ERROR_NONE) {
277 wfd_sink_error("failed to set state : %d\n", result);
281 wfd_sink_debug_fleave();
286 int _mm_wfd_sink_pause(mm_wfd_sink_t *wfd_sink)
288 int result = MM_ERROR_NONE;
290 wfd_sink_debug_fenter();
292 wfd_sink_return_val_if_fail(wfd_sink &&
293 wfd_sink->pipeline &&
294 wfd_sink->pipeline->mainbin &&
295 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst &&
296 wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst,
297 MM_ERROR_WFD_NOT_INITIALIZED);
299 /* check current wi-fi display sink state */
300 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_PAUSE);
302 g_signal_emit_by_name(wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst, "pause", NULL);
304 wfd_sink_debug_fleave();
309 int _mm_wfd_sink_resume(mm_wfd_sink_t *wfd_sink)
311 int result = MM_ERROR_NONE;
313 wfd_sink_debug_fenter();
315 wfd_sink_return_val_if_fail(wfd_sink &&
316 wfd_sink->pipeline &&
317 wfd_sink->pipeline->mainbin &&
318 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst &&
319 wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst,
320 MM_ERROR_WFD_NOT_INITIALIZED);
322 /* check current wi-fi display sink state */
323 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_RESUME);
325 g_signal_emit_by_name(wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst, "resume", NULL);
327 wfd_sink_debug_fleave();
332 int _mm_wfd_sink_disconnect(mm_wfd_sink_t *wfd_sink)
334 int result = MM_ERROR_NONE;
336 wfd_sink_debug_fenter();
338 wfd_sink_return_val_if_fail(wfd_sink &&
339 wfd_sink->pipeline &&
340 wfd_sink->pipeline->mainbin &&
341 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst &&
342 wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst,
343 MM_ERROR_WFD_NOT_INITIALIZED);
345 /* check current wi-fi display sink state */
346 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_DISCONNECT);
348 WFD_SINK_MANAGER_LOCK(wfd_sink) ;
349 WFD_SINK_MANAGER_SIGNAL_CMD(wfd_sink, WFD_SINK_MANAGER_CMD_EXIT);
350 WFD_SINK_MANAGER_UNLOCK(wfd_sink);
352 g_signal_emit_by_name(wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst, "close", NULL);
355 wfd_sink_debug_fleave();
360 int _mm_wfd_sink_unprepare(mm_wfd_sink_t *wfd_sink)
362 int result = MM_ERROR_NONE;
364 wfd_sink_debug_fenter();
366 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
368 /* check current wi-fi display sink state */
369 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_UNPREPARE);
371 WFD_SINK_MANAGER_LOCK(wfd_sink) ;
372 WFD_SINK_MANAGER_SIGNAL_CMD(wfd_sink, WFD_SINK_MANAGER_CMD_EXIT);
373 WFD_SINK_MANAGER_UNLOCK(wfd_sink);
375 /* release pipeline */
376 result = __mm_wfd_sink_destroy_pipeline(wfd_sink);
377 if (result != MM_ERROR_NONE) {
378 wfd_sink_error("failed to destory pipeline\n");
379 return MM_ERROR_WFD_INTERNAL;
381 wfd_sink_debug("success to destory pipeline\n");
385 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_NULL);
387 wfd_sink_debug_fleave();
392 int _mm_wfd_sink_destroy(mm_wfd_sink_t *wfd_sink)
394 int result = MM_ERROR_NONE;
396 wfd_sink_debug_fenter();
398 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
400 /* check current wi-fi display sink state */
401 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_DESTROY);
404 mm_wfd_sink_ini_unload(&wfd_sink->ini);
406 /* release attributes */
407 _mmwfd_deconstruct_attribute(wfd_sink->attrs);
409 if (MM_ERROR_NONE != _mm_wfd_sink_release_manager(wfd_sink)) {
410 wfd_sink_error("failed to release manager\n");
411 return MM_ERROR_WFD_INTERNAL;
416 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_NONE);
418 wfd_sink_debug_fleave();
423 int _mm_wfd_set_message_callback(mm_wfd_sink_t *wfd_sink, MMWFDMessageCallback callback, void *user_data)
425 int result = MM_ERROR_NONE;
427 wfd_sink_debug_fenter();
429 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
431 wfd_sink->msg_cb = callback;
432 wfd_sink->msg_user_data = user_data;
434 wfd_sink_debug_fleave();
439 static int __mm_wfd_sink_init_gstreamer(mm_wfd_sink_t *wfd_sink)
441 int result = MM_ERROR_NONE;
444 static const int max_argc = 50;
448 wfd_sink_debug_fenter();
451 argc = calloc(1, sizeof(gint));
452 argv = calloc(max_argc, sizeof(gchar *));
453 if (!argc || !argv) {
454 wfd_sink_error("failed to allocate memory for wfdsink\n");
456 MMWFDSINK_FREEIF(argv);
457 MMWFDSINK_FREEIF(argc);
459 return MM_ERROR_WFD_NO_FREE_SPACE;
462 /* we would not do fork for scanning plugins */
463 argv[*argc] = g_strdup("--gst-disable-registry-fork");
466 /* check disable registry scan */
467 argv[*argc] = g_strdup("--gst-disable-registry-update");
470 /* check disable segtrap */
471 argv[*argc] = g_strdup("--gst-disable-segtrap");
475 for (i = 0; i < 5; i++) {
476 if (strlen(wfd_sink->ini.gst_param[i]) > 2) {
477 wfd_sink_debug("set %s\n", wfd_sink->ini.gst_param[i]);
478 argv[*argc] = g_strdup(wfd_sink->ini.gst_param[i]);
483 wfd_sink_debug("initializing gstreamer with following parameter\n");
484 wfd_sink_debug("argc : %d\n", *argc);
486 for (i = 0; i < *argc; i++) {
487 wfd_sink_debug("argv[%d] : %s\n", i, argv[i]);
490 /* initializing gstreamer */
491 if (!gst_init_check(argc, &argv, &err)) {
492 wfd_sink_error("failed to initialize gstreamer: %s\n",
493 err ? err->message : "unknown error occurred");
497 result = MM_ERROR_WFD_INTERNAL;
501 for (i = 0; i < *argc; i++) {
502 MMWFDSINK_FREEIF(argv[i]);
504 MMWFDSINK_FREEIF(argv);
505 MMWFDSINK_FREEIF(argc);
507 wfd_sink_debug_fleave();
512 static GstBusSyncReply
513 _mm_wfd_bus_sync_callback(GstBus *bus, GstMessage *message, gpointer data)
515 GstBusSyncReply ret = GST_BUS_PASS;
517 wfd_sink_return_val_if_fail(message &&
518 GST_IS_MESSAGE(message) &&
519 GST_MESSAGE_SRC(message),
522 switch (GST_MESSAGE_TYPE(message)) {
523 case GST_MESSAGE_TAG:
525 case GST_MESSAGE_DURATION:
527 case GST_MESSAGE_STATE_CHANGED: {
528 /* we only handle state change messages from pipeline */
529 if (!GST_IS_PIPELINE(GST_MESSAGE_SRC(message)))
533 case GST_MESSAGE_ASYNC_DONE: {
534 if (!GST_IS_PIPELINE(GST_MESSAGE_SRC(message)))
546 _mm_wfd_sink_msg_callback(GstBus *bus, GstMessage *msg, gpointer data)
548 mm_wfd_sink_t *wfd_sink = (mm_wfd_sink_t *) data;
549 const GstStructure *message_structure = gst_message_get_structure(msg);
552 wfd_sink_return_val_if_fail(wfd_sink, FALSE);
553 wfd_sink_return_val_if_fail(msg && GST_IS_MESSAGE(msg), FALSE);
555 wfd_sink_debug("got %s from %s \n",
556 GST_STR_NULL(GST_MESSAGE_TYPE_NAME(msg)),
557 GST_STR_NULL(GST_OBJECT_NAME(GST_MESSAGE_SRC(msg))));
559 switch (GST_MESSAGE_TYPE(msg)) {
560 case GST_MESSAGE_ERROR: {
561 GError *error = NULL;
565 gst_message_parse_error(msg, &error, &debug);
567 wfd_sink_error("error : %s\n", error->message);
568 wfd_sink_error("debug : %s\n", debug);
570 MMWFDSINK_FREEIF(debug);
575 case GST_MESSAGE_WARNING: {
577 GError *error = NULL;
579 gst_message_parse_warning(msg, &error, &debug);
581 wfd_sink_error("warning : %s\n", error->message);
582 wfd_sink_error("debug : %s\n", debug);
584 MMWFDSINK_FREEIF(debug);
589 case GST_MESSAGE_STATE_CHANGED: {
590 const GValue *voldstate, *vnewstate, *vpending;
591 GstState oldstate, newstate, pending;
592 const GstStructure *structure;
594 /* we only handle messages from pipeline */
595 if (msg->src != (GstObject *)wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst)
598 /* get state info from msg */
599 structure = gst_message_get_structure(msg);
600 if (structure == NULL)
603 voldstate = gst_structure_get_value(structure, "old-state");
604 vnewstate = gst_structure_get_value(structure, "new-state");
605 vpending = gst_structure_get_value(structure, "pending-state");
606 if (voldstate == NULL || vnewstate == NULL || vpending == NULL)
609 oldstate = (GstState)voldstate->data[0].v_int;
610 newstate = (GstState)vnewstate->data[0].v_int;
611 pending = (GstState)vpending->data[0].v_int;
613 wfd_sink_debug("state changed [%s] : %s--->%s final : %s\n",
614 GST_OBJECT_NAME(GST_MESSAGE_SRC(msg)),
615 gst_element_state_get_name((GstState)oldstate),
616 gst_element_state_get_name((GstState)newstate),
617 gst_element_state_get_name((GstState)pending));
619 if (oldstate == newstate) {
620 wfd_sink_error("pipeline reports state transition to old state\n");
625 case GST_STATE_VOID_PENDING:
627 case GST_STATE_READY:
628 case GST_STATE_PAUSED:
629 case GST_STATE_PLAYING:
636 case GST_MESSAGE_CLOCK_LOST: {
637 GstClock *clock = NULL;
638 gst_message_parse_clock_lost(msg, &clock);
639 wfd_sink_debug("The current clock[%s] as selected by the pipeline became unusable.",
640 (clock ? GST_OBJECT_NAME(clock) : "NULL"));
644 case GST_MESSAGE_NEW_CLOCK: {
645 GstClock *clock = NULL;
646 gst_message_parse_new_clock(msg, &clock);
650 if (wfd_sink->clock) {
651 if (wfd_sink->clock != clock)
652 wfd_sink_debug("clock is changed! [%s] -->[%s]\n",
653 GST_STR_NULL(GST_OBJECT_NAME(wfd_sink->clock)),
654 GST_STR_NULL(GST_OBJECT_NAME(clock)));
656 wfd_sink_debug("same clock is selected again! [%s] \n",
657 GST_STR_NULL(GST_OBJECT_NAME(clock)));
659 wfd_sink_debug("new clock [%s] was selected in the pipeline\n",
660 (GST_STR_NULL(GST_OBJECT_NAME(clock))));
663 wfd_sink->clock = clock;
667 case GST_MESSAGE_APPLICATION: {
668 const gchar *message_structure_name;
670 message_structure_name = gst_structure_get_name(message_structure);
671 if (!message_structure_name)
674 wfd_sink_debug("message name : %s", GST_STR_NULL(message_structure_name));
678 case GST_MESSAGE_ELEMENT: {
679 const gchar *structure_name = NULL;
681 structure_name = gst_structure_get_name(message_structure);
682 if (structure_name) {
683 wfd_sink_debug("got element specific message[%s]\n", GST_STR_NULL(structure_name));
684 if (g_strrstr(structure_name, "GstUDPSrcTimeout")) {
685 wfd_sink_error("Got %s, post error message\n", GST_STR_NULL(structure_name));
686 MMWFDSINK_POST_MESSAGE(wfd_sink,
687 MM_ERROR_WFD_INTERNAL,
688 MMWFDSINK_CURRENT_STATE(wfd_sink));
694 case GST_MESSAGE_PROGRESS: {
695 GstProgressType type = GST_PROGRESS_TYPE_ERROR;
696 gchar *category = NULL, *text = NULL;
698 gst_message_parse_progress(msg, &type, &category, &text);
699 wfd_sink_debug("%s : %s \n", GST_STR_NULL(category), GST_STR_NULL(text));
702 case GST_PROGRESS_TYPE_START:
704 case GST_PROGRESS_TYPE_COMPLETE:
705 if (category && !strcmp(category, "open"))
706 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_CONNECTED);
707 else if (category && !strcmp(category, "play")) {
708 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_PLAYING);
709 /*_mm_wfd_sink_correct_pipeline_latency (wfd_sink); */
710 } else if (category && !strcmp(category, "pause"))
711 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_PAUSED);
712 else if (category && !strcmp(category, "close"))
713 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_DISCONNECTED);
717 case GST_PROGRESS_TYPE_CANCELED:
719 case GST_PROGRESS_TYPE_ERROR:
720 if (category && !strcmp(category, "open")) {
721 wfd_sink_error("got error : %s\n", GST_STR_NULL(text));
722 /*_mm_wfd_sink_disconnect (wfd_sink); */
723 MMWFDSINK_POST_MESSAGE(wfd_sink,
724 MM_ERROR_WFD_INTERNAL,
725 MMWFDSINK_CURRENT_STATE(wfd_sink));
726 } else if (category && !strcmp(category, "play")) {
727 wfd_sink_error("got error : %s\n", GST_STR_NULL(text));
728 /*_mm_wfd_sink_disconnect (wfd_sink); */
729 MMWFDSINK_POST_MESSAGE(wfd_sink,
730 MM_ERROR_WFD_INTERNAL,
731 MMWFDSINK_CURRENT_STATE(wfd_sink));
732 } else if (category && !strcmp(category, "pause")) {
733 wfd_sink_error("got error : %s\n", GST_STR_NULL(text));
734 /*_mm_wfd_sink_disconnect (wfd_sink); */
735 MMWFDSINK_POST_MESSAGE(wfd_sink,
736 MM_ERROR_WFD_INTERNAL,
737 MMWFDSINK_CURRENT_STATE(wfd_sink));
738 } else if (category && !strcmp(category, "close")) {
739 wfd_sink_error("got error : %s\n", GST_STR_NULL(text));
740 /*_mm_wfd_sink_disconnect (wfd_sink); */
741 MMWFDSINK_POST_MESSAGE(wfd_sink,
742 MM_ERROR_WFD_INTERNAL,
743 MMWFDSINK_CURRENT_STATE(wfd_sink));
745 wfd_sink_error("got error : %s\n", GST_STR_NULL(text));
749 wfd_sink_error("progress message has no type\n");
753 MMWFDSINK_FREEIF(category);
754 MMWFDSINK_FREEIF(text);
757 case GST_MESSAGE_ASYNC_START:
758 wfd_sink_debug("GST_MESSAGE_ASYNC_START : %s\n", gst_element_get_name(GST_MESSAGE_SRC(msg)));
760 case GST_MESSAGE_ASYNC_DONE:
761 wfd_sink_debug("GST_MESSAGE_ASYNC_DONE : %s\n", gst_element_get_name(GST_MESSAGE_SRC(msg)));
763 case GST_MESSAGE_UNKNOWN:
764 case GST_MESSAGE_INFO:
765 case GST_MESSAGE_TAG:
766 case GST_MESSAGE_BUFFERING:
767 case GST_MESSAGE_EOS:
768 case GST_MESSAGE_STATE_DIRTY:
769 case GST_MESSAGE_STEP_DONE:
770 case GST_MESSAGE_CLOCK_PROVIDE:
771 case GST_MESSAGE_STRUCTURE_CHANGE:
772 case GST_MESSAGE_STREAM_STATUS:
773 case GST_MESSAGE_SEGMENT_START:
774 case GST_MESSAGE_SEGMENT_DONE:
775 case GST_MESSAGE_DURATION:
776 case GST_MESSAGE_LATENCY:
777 case GST_MESSAGE_REQUEST_STATE:
778 case GST_MESSAGE_STEP_START:
779 case GST_MESSAGE_QOS:
780 case GST_MESSAGE_ANY:
783 wfd_sink_debug("unhandled message\n");
791 __mm_wfd_sink_gst_element_add_bucket_to_bin(GstBin *bin, GList *element_bucket, gboolean need_prepare)
793 GList *bucket = element_bucket;
794 MMWFDSinkGstElement *element = NULL;
795 int successful_add_count = 0;
797 wfd_sink_debug_fenter();
799 wfd_sink_return_val_if_fail(element_bucket, 0);
800 wfd_sink_return_val_if_fail(bin, 0);
802 for (; bucket; bucket = bucket->next) {
803 element = (MMWFDSinkGstElement *)bucket->data;
805 if (element && element->gst) {
807 gst_element_set_state(GST_ELEMENT(element->gst), GST_STATE_READY);
809 if (!gst_bin_add(bin, GST_ELEMENT(element->gst))) {
810 wfd_sink_error("failed to add element [%s] to bin [%s]\n",
811 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT(element->gst))),
812 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT_CAST(bin))));
816 wfd_sink_debug("add element [%s] to bin [%s]\n",
817 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT(element->gst))),
818 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT_CAST(bin))));
820 successful_add_count++;
824 wfd_sink_debug_fleave();
826 return successful_add_count;
830 __mm_wfd_sink_gst_element_link_bucket(GList *element_bucket)
832 GList *bucket = element_bucket;
833 MMWFDSinkGstElement *element = NULL;
834 MMWFDSinkGstElement *prv_element = NULL;
835 gint successful_link_count = 0;
837 wfd_sink_debug_fenter();
839 wfd_sink_return_val_if_fail(element_bucket, -1);
841 prv_element = (MMWFDSinkGstElement *)bucket->data;
842 bucket = bucket->next;
844 for (; bucket; bucket = bucket->next) {
845 element = (MMWFDSinkGstElement *)bucket->data;
847 if (element && element->gst) {
848 if (gst_element_link(GST_ELEMENT(prv_element->gst), GST_ELEMENT(element->gst))) {
849 wfd_sink_debug("linking [%s] to [%s] success\n",
850 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT(prv_element->gst))),
851 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT(element->gst))));
852 successful_link_count++;
854 wfd_sink_error("linking [%s] to [%s] failed\n",
855 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT(prv_element->gst))),
856 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT(element->gst))));
861 prv_element = element;
864 wfd_sink_debug_fleave();
866 return successful_link_count;
870 __mm_wfd_sink_check_state(mm_wfd_sink_t *wfd_sink, MMWFDSinkCommandType cmd)
872 MMWFDSinkStateType cur_state = MM_WFD_SINK_STATE_NONE;
874 wfd_sink_debug_fenter();
876 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
878 MMWFDSINK_PRINT_STATE(wfd_sink);
880 cur_state = MMWFDSINK_CURRENT_STATE(wfd_sink);
883 case MM_WFD_SINK_COMMAND_CREATE: {
884 if (cur_state != MM_WFD_SINK_STATE_NONE)
887 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_NULL;
891 case MM_WFD_SINK_COMMAND_PREPARE: {
892 if (cur_state == MM_WFD_SINK_STATE_PREPARED)
894 else if (cur_state != MM_WFD_SINK_STATE_NULL)
897 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_PREPARED;
901 case MM_WFD_SINK_COMMAND_CONNECT: {
902 if (cur_state == MM_WFD_SINK_STATE_CONNECTED)
904 else if (cur_state != MM_WFD_SINK_STATE_PREPARED)
907 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_CONNECTED;
911 case MM_WFD_SINK_COMMAND_START: {
912 if (cur_state == MM_WFD_SINK_STATE_PLAYING)
914 else if (cur_state != MM_WFD_SINK_STATE_CONNECTED)
917 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_PLAYING;
921 case MM_WFD_SINK_COMMAND_PAUSE: {
922 if (cur_state == MM_WFD_SINK_STATE_PAUSED)
924 else if (cur_state != MM_WFD_SINK_STATE_PLAYING)
927 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_PAUSED;
931 case MM_WFD_SINK_COMMAND_RESUME: {
932 if (cur_state == MM_WFD_SINK_STATE_PLAYING)
934 else if (cur_state != MM_WFD_SINK_STATE_PAUSED)
937 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_PLAYING;
941 case MM_WFD_SINK_COMMAND_DISCONNECT: {
942 if (cur_state == MM_WFD_SINK_STATE_NONE ||
943 cur_state == MM_WFD_SINK_STATE_NULL ||
944 cur_state == MM_WFD_SINK_STATE_PREPARED ||
945 cur_state == MM_WFD_SINK_STATE_DISCONNECTED)
947 else if (cur_state != MM_WFD_SINK_STATE_PLAYING &&
948 cur_state != MM_WFD_SINK_STATE_CONNECTED &&
949 cur_state != MM_WFD_SINK_STATE_PAUSED)
952 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_DISCONNECTED;
956 case MM_WFD_SINK_COMMAND_UNPREPARE: {
957 if (cur_state == MM_WFD_SINK_STATE_NONE ||
958 cur_state == MM_WFD_SINK_STATE_NULL)
961 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_NULL;
965 case MM_WFD_SINK_COMMAND_DESTROY: {
966 if (cur_state == MM_WFD_SINK_STATE_NONE)
969 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_NONE;
979 wfd_sink_debug_fleave();
981 return MM_ERROR_NONE;
984 wfd_sink_debug("already %s state, nothing to do.\n", MMWFDSINK_STATE_GET_NAME(cur_state));
985 return MM_ERROR_WFD_NO_OP;
989 wfd_sink_error("current state is invalid.\n", MMWFDSINK_STATE_GET_NAME(cur_state));
990 return MM_ERROR_WFD_INVALID_STATE;
993 static int __mm_wfd_sink_set_state(mm_wfd_sink_t *wfd_sink, MMWFDSinkStateType state)
995 wfd_sink_debug_fenter();
997 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
999 if (MMWFDSINK_CURRENT_STATE(wfd_sink) == state) {
1000 wfd_sink_error("already state(%s)\n", MMWFDSINK_STATE_GET_NAME(state));
1001 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_NONE;
1002 return MM_ERROR_NONE;
1005 /* update wi-fi display state */
1006 MMWFDSINK_PREVIOUS_STATE(wfd_sink) = MMWFDSINK_CURRENT_STATE(wfd_sink);
1007 MMWFDSINK_CURRENT_STATE(wfd_sink) = state;
1009 if (MMWFDSINK_CURRENT_STATE(wfd_sink) == MMWFDSINK_PENDING_STATE(wfd_sink))
1010 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_NONE;
1012 /* poset state message to application */
1013 MMWFDSINK_POST_MESSAGE(wfd_sink,
1015 MMWFDSINK_CURRENT_STATE(wfd_sink));
1018 MMWFDSINK_PRINT_STATE(wfd_sink);
1020 wfd_sink_debug_fleave();
1022 return MM_ERROR_NONE;
1026 __mm_wfd_sink_set_pipeline_state(mm_wfd_sink_t *wfd_sink, GstState state, gboolean async)
1028 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
1029 GstState cur_state = GST_STATE_VOID_PENDING;
1030 GstState pending_state = GST_STATE_VOID_PENDING;
1032 wfd_sink_debug_fenter();
1034 wfd_sink_return_val_if_fail(wfd_sink &&
1035 wfd_sink->pipeline &&
1036 wfd_sink->pipeline->mainbin &&
1037 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst,
1038 MM_ERROR_WFD_NOT_INITIALIZED);
1040 wfd_sink_return_val_if_fail(state > GST_STATE_VOID_PENDING,
1041 MM_ERROR_WFD_INVALID_ARGUMENT);
1043 wfd_sink_debug("try to set %s state \n", gst_element_state_get_name(state));
1045 result = gst_element_set_state(wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst, state);
1046 if (result == GST_STATE_CHANGE_FAILURE) {
1047 wfd_sink_error("fail to set %s state....\n", gst_element_state_get_name(state));
1048 return MM_ERROR_WFD_INTERNAL;
1052 wfd_sink_debug("wait for changing state is completed \n");
1054 result = gst_element_get_state(wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst,
1055 &cur_state, &pending_state, wfd_sink->ini.state_change_timeout * GST_SECOND);
1056 if (result == GST_STATE_CHANGE_FAILURE) {
1057 wfd_sink_error("fail to get state within %d seconds....\n", wfd_sink->ini.state_change_timeout);
1059 __mm_wfd_sink_dump_pipeline_state(wfd_sink);
1061 return MM_ERROR_WFD_INTERNAL;
1062 } else if (result == GST_STATE_CHANGE_NO_PREROLL) {
1063 wfd_sink_debug("successfully changed state but is not able to provide data yet\n");
1066 wfd_sink_debug("cur state is %s, pending state is %s\n",
1067 gst_element_state_get_name(cur_state),
1068 gst_element_state_get_name(pending_state));
1072 wfd_sink_debug_fleave();
1074 return MM_ERROR_NONE;
1078 _mm_wfd_sink_reset_basetime(mm_wfd_sink_t *wfd_sink)
1080 GstClockTime base_time = GST_CLOCK_TIME_NONE;
1083 wfd_sink_debug_fenter();
1085 wfd_sink_return_if_fail(wfd_sink &&
1086 wfd_sink->pipeline &&
1087 wfd_sink->pipeline->mainbin &&
1088 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst);
1089 wfd_sink_return_if_fail(wfd_sink->need_to_reset_basetime);
1092 if (wfd_sink->clock)
1093 base_time = gst_clock_get_time(wfd_sink->clock);
1095 if (GST_CLOCK_TIME_IS_VALID(base_time)) {
1097 wfd_sink_debug("set pipeline base_time as now [%"GST_TIME_FORMAT"]\n", GST_TIME_ARGS(base_time));
1099 for (i = 0; i < WFD_SINK_M_NUM; i++) {
1100 if (wfd_sink->pipeline->mainbin[i].gst)
1101 gst_element_set_base_time(GST_ELEMENT_CAST(wfd_sink->pipeline->mainbin[i].gst), base_time);
1104 if (wfd_sink->pipeline->videobin) {
1105 for (i = 0; i < WFD_SINK_V_NUM; i++) {
1106 if (wfd_sink->pipeline->videobin[i].gst)
1107 gst_element_set_base_time(GST_ELEMENT_CAST(wfd_sink->pipeline->videobin[i].gst), base_time);
1111 if (wfd_sink->pipeline->audiobin) {
1112 for (i = 0; i < WFD_SINK_A_NUM; i++) {
1113 if (wfd_sink->pipeline->audiobin[i].gst)
1114 gst_element_set_base_time(GST_ELEMENT_CAST(wfd_sink->pipeline->audiobin[i].gst), base_time);
1117 wfd_sink->need_to_reset_basetime = FALSE;
1120 wfd_sink_debug_fleave();
1126 __mm_wfd_sink_prepare_videobin(mm_wfd_sink_t *wfd_sink)
1128 GstElement *videobin = NULL;
1130 wfd_sink_debug_fenter();
1132 wfd_sink_return_val_if_fail(wfd_sink &&
1134 MM_ERROR_WFD_NOT_INITIALIZED);
1136 if (wfd_sink->pipeline->videobin == NULL) {
1137 if (MM_ERROR_NONE != __mm_wfd_sink_create_videobin(wfd_sink)) {
1138 wfd_sink_error("failed to create videobin....\n");
1143 if (!wfd_sink->video_bin_is_linked) {
1144 if (MM_ERROR_NONE != __mm_wfd_sink_link_videobin(wfd_sink)) {
1145 wfd_sink_error("failed to link video decoder.....\n");
1150 videobin = wfd_sink->pipeline->videobin[WFD_SINK_V_BIN].gst;
1152 if (GST_STATE(videobin) <= GST_STATE_NULL) {
1153 if (GST_STATE_CHANGE_FAILURE == gst_element_set_state(videobin, GST_STATE_READY)) {
1154 wfd_sink_error("failed to set state(READY) to %s\n", GST_STR_NULL(GST_ELEMENT_NAME(videobin)));
1159 wfd_sink_debug_fleave();
1161 return MM_ERROR_NONE;
1165 /* need to notify to app */
1166 MMWFDSINK_POST_MESSAGE(wfd_sink,
1167 MM_ERROR_WFD_INTERNAL,
1168 MMWFDSINK_CURRENT_STATE(wfd_sink));
1170 return MM_ERROR_WFD_INTERNAL;
1174 __mm_wfd_sink_prepare_audiobin(mm_wfd_sink_t *wfd_sink)
1176 MMWFDSinkGstElement *audiobin = NULL;
1178 wfd_sink_debug_fenter();
1180 wfd_sink_return_val_if_fail(wfd_sink &&
1182 MM_ERROR_WFD_NOT_INITIALIZED);
1184 if (wfd_sink->pipeline->audiobin == NULL) {
1185 if (MM_ERROR_NONE != __mm_wfd_sink_create_audiobin(wfd_sink)) {
1186 wfd_sink_error("failed to create audiobin....\n");
1191 if (!wfd_sink->audio_bin_is_linked) {
1192 if (MM_ERROR_NONE != __mm_wfd_sink_link_audiobin(wfd_sink)) {
1193 wfd_sink_error("failed to link audio decoder.....\n");
1198 audiobin = wfd_sink->pipeline->audiobin;
1200 if (GST_STATE(audiobin) <= GST_STATE_NULL) {
1201 if (GST_STATE_CHANGE_FAILURE == gst_element_set_state(audiobin[WFD_SINK_A_BIN].gst, GST_STATE_READY)) {
1202 wfd_sink_error("failed to set state(READY) to %s\n",
1203 GST_STR_NULL(GST_ELEMENT_NAME(audiobin)));
1208 wfd_sink_debug_fleave();
1210 return MM_ERROR_NONE;
1214 /* need to notify to app */
1215 MMWFDSINK_POST_MESSAGE(wfd_sink,
1216 MM_ERROR_WFD_INTERNAL,
1217 MMWFDSINK_CURRENT_STATE(wfd_sink));
1219 return MM_ERROR_WFD_INTERNAL;
1222 #define COMPENSATION_CRETERIA_VALUE 1000000 /* 1 msec */
1223 #define COMPENSATION_CHECK_PERIOD (30*GST_SECOND) /* 30 sec */
1225 static GstPadProbeReturn
1226 _mm_wfd_sink_check_running_time(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1228 mm_wfd_sink_t *wfd_sink = (mm_wfd_sink_t *)u_data;
1229 GstClockTime current_time = GST_CLOCK_TIME_NONE;
1230 GstClockTime start_time = GST_CLOCK_TIME_NONE;
1231 GstClockTime running_time = GST_CLOCK_TIME_NONE;
1232 GstClockTime base_time = GST_CLOCK_TIME_NONE;
1233 GstClockTime render_time = GST_CLOCK_TIME_NONE;
1234 GstClockTimeDiff diff = GST_CLOCK_TIME_NONE;
1235 GstBuffer *buffer = NULL;
1236 gint64 ts_offset = 0LL;
1238 wfd_sink_return_val_if_fail(info, FALSE);
1239 wfd_sink_return_val_if_fail(wfd_sink &&
1240 wfd_sink->pipeline &&
1241 wfd_sink->pipeline->mainbin &&
1242 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst,
1243 GST_PAD_PROBE_DROP);
1245 if (!wfd_sink->clock) {
1246 wfd_sink_warning("pipeline did not select clock, yet\n");
1247 return GST_PAD_PROBE_OK;
1250 if (wfd_sink->need_to_reset_basetime)
1251 _mm_wfd_sink_reset_basetime(wfd_sink);
1253 /* calculate current runninig time */
1254 current_time = gst_clock_get_time(wfd_sink->clock);
1255 if (g_strrstr(GST_OBJECT_NAME(pad), "video"))
1256 base_time = gst_element_get_base_time(wfd_sink->pipeline->videobin[WFD_SINK_V_BIN].gst);
1257 else if (g_strrstr(GST_OBJECT_NAME(pad), "audio"))
1258 base_time = gst_element_get_base_time(wfd_sink->pipeline->audiobin[WFD_SINK_A_BIN].gst);
1259 start_time = gst_element_get_start_time(wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst);
1260 if (GST_CLOCK_TIME_IS_VALID(current_time) &&
1261 GST_CLOCK_TIME_IS_VALID(start_time) &&
1262 GST_CLOCK_TIME_IS_VALID(base_time)) {
1263 running_time = current_time - (start_time + base_time);
1265 wfd_sink_debug("current time %"GST_TIME_FORMAT", start time %"GST_TIME_FORMAT
1266 " base time %"GST_TIME_FORMAT"\n", GST_TIME_ARGS(current_time),
1267 GST_TIME_ARGS(start_time), GST_TIME_ARGS(base_time));
1268 return GST_PAD_PROBE_OK;
1271 /* calculate this buffer rendering time */
1272 buffer = gst_pad_probe_info_get_buffer(info);
1273 if (!GST_BUFFER_TIMESTAMP_IS_VALID(buffer)) {
1274 wfd_sink_error("buffer timestamp is invalid.\n");
1275 return GST_PAD_PROBE_OK;
1278 if (g_strrstr(GST_OBJECT_NAME(pad), "audio")) {
1279 if (wfd_sink->pipeline && wfd_sink->pipeline->audiobin && wfd_sink->pipeline->audiobin[WFD_SINK_A_SINK].gst)
1280 g_object_get(G_OBJECT(wfd_sink->pipeline->audiobin[WFD_SINK_A_SINK].gst), "ts-offset", &ts_offset, NULL);
1281 } else if (g_strrstr(GST_OBJECT_NAME(pad), "video")) {
1282 if (wfd_sink->pipeline && wfd_sink->pipeline->videobin && wfd_sink->pipeline->videobin[WFD_SINK_V_SINK].gst)
1283 g_object_get(G_OBJECT(wfd_sink->pipeline->videobin[WFD_SINK_V_SINK].gst), "ts-offset", &ts_offset, NULL);
1286 render_time = GST_BUFFER_TIMESTAMP(buffer);
1287 render_time += ts_offset;
1289 /* chekc this buffer could be rendered or not */
1290 if (GST_CLOCK_TIME_IS_VALID(running_time) && GST_CLOCK_TIME_IS_VALID(render_time)) {
1291 diff = GST_CLOCK_DIFF(running_time, render_time);
1293 /* this buffer could be NOT rendered */
1294 wfd_sink_debug("%s : diff time : -%" GST_TIME_FORMAT "\n",
1295 GST_STR_NULL((GST_OBJECT_NAME(pad))),
1296 GST_TIME_ARGS(GST_CLOCK_DIFF(render_time, running_time)));
1298 /* this buffer could be rendered */
1299 /*wfd_sink_debug ("%s :diff time : %" GST_TIME_FORMAT "\n", */
1300 /* GST_STR_NULL((GST_OBJECT_NAME(pad))), */
1301 /* GST_TIME_ARGS(diff)); */
1305 /* update buffer count and gap */
1306 if (g_strrstr(GST_OBJECT_NAME(pad), "video")) {
1307 wfd_sink->video_buffer_count++;
1308 wfd_sink->video_accumulated_gap += diff;
1309 } else if (g_strrstr(GST_OBJECT_NAME(pad), "audio")) {
1310 wfd_sink->audio_buffer_count++;
1311 wfd_sink->audio_accumulated_gap += diff;
1313 wfd_sink_warning("invalid buffer type.. \n");
1314 return GST_PAD_PROBE_DROP;
1317 if (GST_CLOCK_TIME_IS_VALID(wfd_sink->last_buffer_timestamp)) {
1318 /* fisrt 60sec, just calculate the gap between source device and sink device */
1319 if (GST_BUFFER_TIMESTAMP(buffer) < 60 * GST_SECOND)
1320 return GST_PAD_PROBE_OK;
1322 /* every 10sec, calculate the gap between source device and sink device */
1323 if (GST_CLOCK_DIFF(wfd_sink->last_buffer_timestamp, GST_BUFFER_TIMESTAMP(buffer))
1324 > COMPENSATION_CHECK_PERIOD) {
1325 gint64 audio_avgrage_gap = 0LL;
1326 gint64 video_avgrage_gap = 0LL;
1327 gint64 audio_avgrage_gap_diff = 0LL;
1328 gint64 video_avgrage_gap_diff = 0LL;
1329 gboolean video_minus_compensation = FALSE;
1330 gboolean audio_minus_compensation = FALSE;
1331 gint64 avgrage_gap_diff = 0LL;
1332 gboolean minus_compensation = FALSE;
1335 if (wfd_sink->video_buffer_count > 0) {
1336 video_avgrage_gap = wfd_sink->video_accumulated_gap / wfd_sink->video_buffer_count;
1338 if (wfd_sink->video_average_gap != 0) {
1339 if (video_avgrage_gap > wfd_sink->video_average_gap) {
1340 video_avgrage_gap_diff = video_avgrage_gap - wfd_sink->video_average_gap;
1341 video_minus_compensation = TRUE;
1343 video_avgrage_gap_diff = wfd_sink->video_average_gap - video_avgrage_gap;
1344 video_minus_compensation = FALSE;
1347 wfd_sink_debug("first update video average gap(%lld) \n", video_avgrage_gap);
1348 wfd_sink->video_average_gap = video_avgrage_gap;
1351 wfd_sink_debug("there is no video buffer flow during %"GST_TIME_FORMAT
1352 " ~ %" GST_TIME_FORMAT"\n",
1353 GST_TIME_ARGS(wfd_sink->last_buffer_timestamp),
1354 GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(buffer)));
1358 if (wfd_sink->audio_buffer_count > 0) {
1359 audio_avgrage_gap = wfd_sink->audio_accumulated_gap / wfd_sink->audio_buffer_count;
1361 if (wfd_sink->audio_average_gap != 0) {
1362 if (audio_avgrage_gap > wfd_sink->audio_average_gap) {
1363 audio_avgrage_gap_diff = audio_avgrage_gap - wfd_sink->audio_average_gap;
1364 audio_minus_compensation = TRUE;
1366 audio_avgrage_gap_diff = wfd_sink->audio_average_gap - audio_avgrage_gap;
1367 audio_minus_compensation = FALSE;
1370 wfd_sink_debug("first update audio average gap(%lld) \n", audio_avgrage_gap);
1371 wfd_sink->audio_average_gap = audio_avgrage_gap;
1374 wfd_sink_debug("there is no audio buffer flow during %"GST_TIME_FORMAT
1375 " ~ %" GST_TIME_FORMAT"\n",
1376 GST_TIME_ARGS(wfd_sink->last_buffer_timestamp),
1377 GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(buffer)));
1380 /* selecet average_gap_diff between video and audio */
1381 /* which makes no buffer drop in the sink elements */
1382 if (video_avgrage_gap_diff && audio_avgrage_gap_diff) {
1383 if (!video_minus_compensation && !audio_minus_compensation) {
1384 minus_compensation = FALSE;
1385 if (video_avgrage_gap_diff > audio_avgrage_gap_diff)
1386 avgrage_gap_diff = video_avgrage_gap_diff;
1388 avgrage_gap_diff = audio_avgrage_gap_diff;
1389 } else if (video_minus_compensation && audio_minus_compensation) {
1390 minus_compensation = TRUE;
1391 if (video_avgrage_gap_diff > audio_avgrage_gap_diff)
1392 avgrage_gap_diff = audio_avgrage_gap_diff;
1394 avgrage_gap_diff = video_avgrage_gap_diff;
1396 minus_compensation = FALSE;
1397 if (!video_minus_compensation)
1398 avgrage_gap_diff = video_avgrage_gap_diff;
1400 avgrage_gap_diff = audio_avgrage_gap_diff;
1402 } else if (video_avgrage_gap_diff) {
1403 minus_compensation = video_minus_compensation;
1404 avgrage_gap_diff = video_avgrage_gap_diff;
1405 } else if (audio_avgrage_gap_diff) {
1406 minus_compensation = audio_minus_compensation;
1407 avgrage_gap_diff = audio_avgrage_gap_diff;
1410 wfd_sink_debug("average diff gap difference beween audio:%s%lld and video:%s%lld \n",
1411 audio_minus_compensation ? "-" : "", audio_avgrage_gap_diff,
1412 video_minus_compensation ? "-" : "", video_avgrage_gap_diff);
1415 /* if calculated gap diff is larger than 1ms. need to compensate buffer timestamp */
1416 if (avgrage_gap_diff >= COMPENSATION_CRETERIA_VALUE) {
1417 if (minus_compensation)
1418 ts_offset -= avgrage_gap_diff;
1420 ts_offset += avgrage_gap_diff;
1422 wfd_sink_debug("do timestamp compensation : %s%lld (ts-offset : %"
1423 GST_TIME_FORMAT") at(%" GST_TIME_FORMAT")\n",
1424 minus_compensation ? "-" : "", avgrage_gap_diff,
1425 GST_TIME_ARGS(ts_offset), GST_TIME_ARGS(running_time));
1427 if (wfd_sink->pipeline && wfd_sink->pipeline->audiobin && wfd_sink->pipeline->audiobin[WFD_SINK_A_SINK].gst)
1428 g_object_set(G_OBJECT(wfd_sink->pipeline->audiobin[WFD_SINK_A_SINK].gst), "ts-offset", (gint64)ts_offset, NULL);
1429 if (wfd_sink->pipeline && wfd_sink->pipeline->videobin && wfd_sink->pipeline->videobin[WFD_SINK_V_SINK].gst)
1430 g_object_set(G_OBJECT(wfd_sink->pipeline->videobin[WFD_SINK_V_SINK].gst), "ts-offset", (gint64)ts_offset, NULL);
1432 wfd_sink_debug("don't need to do timestamp compensation : %s%lld (ts-offset : %"GST_TIME_FORMAT ")\n",
1433 minus_compensation ? "-" : "", avgrage_gap_diff, GST_TIME_ARGS(ts_offset));
1437 wfd_sink->video_buffer_count = 0;
1438 wfd_sink->video_accumulated_gap = 0LL;
1439 wfd_sink->audio_buffer_count = 0;
1440 wfd_sink->audio_accumulated_gap = 0LL;
1441 wfd_sink->last_buffer_timestamp = GST_BUFFER_TIMESTAMP(buffer);
1444 wfd_sink_debug("first update last buffer timestamp :%" GST_TIME_FORMAT" \n",
1445 GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(buffer)));
1446 wfd_sink->last_buffer_timestamp = GST_BUFFER_TIMESTAMP(buffer);
1449 return GST_PAD_PROBE_OK;
1454 __mm_wfd_sink_demux_pad_added(GstElement *ele, GstPad *pad, gpointer data)
1456 mm_wfd_sink_t *wfd_sink = (mm_wfd_sink_t *)data;
1457 gchar *name = gst_pad_get_name(pad);
1458 GstElement *sinkbin = NULL;
1459 GstPad *sinkpad = NULL;
1461 wfd_sink_debug_fenter();
1463 wfd_sink_return_if_fail(wfd_sink && wfd_sink->pipeline);
1465 if (name[0] == 'v') {
1466 wfd_sink_debug("=========== >>>>>>>>>> Received VIDEO pad...\n");
1468 MMWFDSINK_PAD_PROBE(wfd_sink, pad, NULL, NULL);
1470 gst_pad_add_probe(pad,
1471 GST_PAD_PROBE_TYPE_BUFFER,
1472 _mm_wfd_sink_check_running_time,
1476 if (GST_STATE(wfd_sink->pipeline->videobin[WFD_SINK_V_BIN].gst) <= GST_STATE_NULL) {
1477 wfd_sink_debug("need to prepare videobin");
1478 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_videobin(wfd_sink)) {
1479 wfd_sink_error("failed to prepare videobin....\n");
1484 sinkbin = wfd_sink->pipeline->videobin[WFD_SINK_V_BIN].gst;
1486 wfd_sink->added_av_pad_num++;
1487 } else if (name[0] == 'a') {
1488 wfd_sink_debug("=========== >>>>>>>>>> Received AUDIO pad...\n");
1490 MMWFDSINK_PAD_PROBE(wfd_sink, pad, NULL, NULL);
1492 gst_pad_add_probe(pad,
1493 GST_PAD_PROBE_TYPE_BUFFER,
1494 _mm_wfd_sink_check_running_time,
1498 if (GST_STATE(wfd_sink->pipeline->audiobin[WFD_SINK_A_BIN].gst) <= GST_STATE_NULL) {
1499 wfd_sink_debug("need to prepare audiobin");
1500 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_audiobin(wfd_sink)) {
1501 wfd_sink_error("failed to prepare audiobin....\n");
1506 sinkbin = wfd_sink->pipeline->audiobin[WFD_SINK_A_BIN].gst;
1508 wfd_sink->added_av_pad_num++;
1510 wfd_sink_error("not handling.....\n\n\n");
1515 wfd_sink_debug("add %s to pipeline.\n",
1516 GST_STR_NULL(GST_ELEMENT_NAME(sinkbin)));
1519 if (!gst_bin_add(GST_BIN(wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst), sinkbin)) {
1520 wfd_sink_error("failed to add sinkbin to pipeline\n");
1524 wfd_sink_debug("link %s .\n", GST_STR_NULL(GST_ELEMENT_NAME(sinkbin)));
1527 sinkpad = gst_element_get_static_pad(GST_ELEMENT_CAST(sinkbin), "sink");
1529 wfd_sink_error("failed to get pad from sinkbin\n");
1533 if (GST_PAD_LINK_OK != gst_pad_link_full(pad, sinkpad, GST_PAD_LINK_CHECK_NOTHING)) {
1534 wfd_sink_error("failed to link sinkbin\n");
1538 wfd_sink_debug("sync state %s with pipeline .\n",
1539 GST_STR_NULL(GST_ELEMENT_NAME(sinkbin)));
1542 if (!gst_element_sync_state_with_parent(GST_ELEMENT_CAST(sinkbin))) {
1543 wfd_sink_error("failed to sync sinkbin state with parent \n");
1547 gst_object_unref(GST_OBJECT(sinkpad));
1552 if (wfd_sink->added_av_pad_num == 2) {
1553 wfd_sink_debug("whole pipeline is constructed. \n");
1555 /* generate dot file of the constructed pipeline of wifi display sink */
1556 MMWFDSINK_GENERATE_DOT_IF_ENABLED(wfd_sink, "constructed-pipeline");
1559 MMWFDSINK_FREEIF(name);
1561 wfd_sink_debug_fleave();
1567 MMWFDSINK_FREEIF(name);
1570 gst_object_unref(GST_OBJECT(sinkpad));
1573 /* need to notify to app */
1574 MMWFDSINK_POST_MESSAGE(wfd_sink,
1575 MM_ERROR_WFD_INTERNAL,
1576 MMWFDSINK_CURRENT_STATE(wfd_sink));
1582 __mm_wfd_sink_change_av_format(GstElement *wfdrtspsrc, gpointer *need_to_flush, gpointer data)
1584 mm_wfd_sink_t *wfd_sink = (mm_wfd_sink_t *)data;
1586 wfd_sink_debug_fenter();
1588 wfd_sink_return_if_fail(wfd_sink);
1589 wfd_sink_return_if_fail(need_to_flush);
1591 if (MMWFDSINK_CURRENT_STATE(wfd_sink) == MM_WFD_SINK_STATE_PLAYING) {
1592 wfd_sink_debug("need to flush pipeline");
1593 *need_to_flush = (gpointer) TRUE;
1595 wfd_sink_debug("don't need to flush pipeline");
1596 *need_to_flush = (gpointer) FALSE;
1600 wfd_sink_debug_fleave();
1605 __mm_wfd_sink_update_stream_info(GstElement *wfdrtspsrc, GstStructure *str, gpointer data)
1607 mm_wfd_sink_t *wfd_sink = (mm_wfd_sink_t *)data;
1608 WFDSinkManagerCMDType cmd = WFD_SINK_MANAGER_CMD_NONE;
1609 MMWFDSinkStreamInfo *stream_info = NULL;
1610 gint is_valid_audio_format = FALSE;
1611 gint is_valid_video_format = FALSE;
1612 gchar *audio_format;
1613 gchar *video_format;
1615 wfd_sink_debug_fenter();
1617 wfd_sink_return_if_fail(str && GST_IS_STRUCTURE(str));
1618 wfd_sink_return_if_fail(wfd_sink);
1620 stream_info = &wfd_sink->stream_info;
1622 if (gst_structure_has_field(str, "audio_format")) {
1623 is_valid_audio_format = TRUE;
1624 audio_format = g_strdup(gst_structure_get_string(str, "audio_format"));
1625 if (g_strrstr(audio_format, "AAC"))
1626 stream_info->audio_stream_info.codec = MM_WFD_SINK_AUDIO_CODEC_AAC;
1627 else if (g_strrstr(audio_format, "AC3"))
1628 stream_info->audio_stream_info.codec = MM_WFD_SINK_AUDIO_CODEC_AC3;
1629 else if (g_strrstr(audio_format, "LPCM"))
1630 stream_info->audio_stream_info.codec = MM_WFD_SINK_AUDIO_CODEC_LPCM;
1632 wfd_sink_error("invalid audio format(%s)...\n", audio_format);
1633 is_valid_audio_format = FALSE;
1636 if (is_valid_audio_format == TRUE) {
1637 if (gst_structure_has_field(str, "audio_rate"))
1638 gst_structure_get_int(str, "audio_rate", &stream_info->audio_stream_info.sample_rate);
1639 if (gst_structure_has_field(str, "audio_channels"))
1640 gst_structure_get_int(str, "audio_channels", &stream_info->audio_stream_info.channels);
1641 if (gst_structure_has_field(str, "audio_bitwidth"))
1642 gst_structure_get_int(str, "audio_bitwidth", &stream_info->audio_stream_info.bitwidth);
1644 cmd = cmd | WFD_SINK_MANAGER_CMD_LINK_A_BIN;
1646 wfd_sink_debug("audio_format : %s \n \t rate : %d \n \t channels : %d \n \t bitwidth : %d \n \t \n",
1648 stream_info->audio_stream_info.sample_rate,
1649 stream_info->audio_stream_info.channels,
1650 stream_info->audio_stream_info.bitwidth);
1654 if (gst_structure_has_field(str, "video_format")) {
1655 is_valid_video_format = TRUE;
1656 video_format = g_strdup(gst_structure_get_string(str, "video_format"));
1657 if (!g_strrstr(video_format, "H264")) {
1658 wfd_sink_error("invalid video format(%s)...\n", video_format);
1659 is_valid_video_format = FALSE;
1662 if (is_valid_video_format == TRUE) {
1663 stream_info->video_stream_info.codec = MM_WFD_SINK_VIDEO_CODEC_H264;
1665 if (gst_structure_has_field(str, "video_width"))
1666 gst_structure_get_int(str, "video_width", &stream_info->video_stream_info.width);
1667 if (gst_structure_has_field(str, "video_height"))
1668 gst_structure_get_int(str, "video_height", &stream_info->video_stream_info.height);
1669 if (gst_structure_has_field(str, "video_framerate"))
1670 gst_structure_get_int(str, "video_framerate", &stream_info->video_stream_info.frame_rate);
1672 cmd = cmd | WFD_SINK_MANAGER_CMD_LINK_V_BIN;
1674 wfd_sink_debug("video_format : %s \n \t width : %d \n \t height : %d \n \t frame_rate : %d \n \t \n",
1676 stream_info->video_stream_info.width,
1677 stream_info->video_stream_info.height,
1678 stream_info->video_stream_info.frame_rate);
1682 if (cmd != WFD_SINK_MANAGER_CMD_NONE) {
1683 WFD_SINK_MANAGER_LOCK(wfd_sink);
1684 WFD_SINK_MANAGER_SIGNAL_CMD(wfd_sink, cmd);
1685 WFD_SINK_MANAGER_UNLOCK(wfd_sink);
1688 wfd_sink_debug_fleave();
1691 static int __mm_wfd_sink_prepare_wfdrtspsrc(mm_wfd_sink_t *wfd_sink, GstElement *wfdrtspsrc)
1693 GstStructure *audio_param = NULL;
1694 GstStructure *video_param = NULL;
1695 GstStructure *hdcp_param = NULL;
1696 void *hdcp_handle = NULL;
1697 gint hdcp_version = 0;
1699 guint CEA_resolution = 0;
1700 guint VESA_resolution = 0;
1701 guint HH_resolution = 0;
1703 wfd_sink_debug_fenter();
1705 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
1706 wfd_sink_return_val_if_fail(wfd_sink->attrs, MM_ERROR_WFD_NOT_INITIALIZED);
1707 wfd_sink_return_val_if_fail(wfdrtspsrc, MM_ERROR_WFD_NOT_INITIALIZED);
1709 g_object_set(G_OBJECT(wfdrtspsrc), "debug", wfd_sink->ini.set_debug_property, NULL);
1710 g_object_set(G_OBJECT(wfdrtspsrc), "latency", wfd_sink->ini.jitter_buffer_latency, NULL);
1711 g_object_set(G_OBJECT(wfdrtspsrc), "do-request", wfd_sink->ini.enable_retransmission, NULL);
1712 g_object_set(G_OBJECT(wfdrtspsrc), "udp-buffer-size", 2097152, NULL);
1713 g_object_set(G_OBJECT(wfdrtspsrc), "enable-pad-probe", wfd_sink->ini.enable_wfdrtspsrc_pad_probe, NULL);
1715 audio_param = gst_structure_new("audio_param",
1716 "audio_codec", G_TYPE_UINT, wfd_sink->ini.audio_codec,
1717 "audio_latency", G_TYPE_UINT, wfd_sink->ini.audio_latency,
1718 "audio_channels", G_TYPE_UINT, wfd_sink->ini.audio_channel,
1719 "audio_sampling_frequency", G_TYPE_UINT, wfd_sink->ini.audio_sampling_frequency,
1722 CEA_resolution = wfd_sink->ini.video_cea_support;
1723 VESA_resolution = wfd_sink->ini.video_vesa_support;
1724 HH_resolution = wfd_sink->ini.video_hh_support;
1726 __mm_wfd_sink_prepare_video_resolution(wfd_sink->supportive_resolution,
1727 &CEA_resolution, &VESA_resolution, &HH_resolution);
1729 wfd_sink_debug("set video resolution CEA[%x] VESA[%x] HH[%x]", CEA_resolution, VESA_resolution, HH_resolution);
1731 video_param = gst_structure_new("video_param",
1732 "video_codec", G_TYPE_UINT, wfd_sink->ini.video_codec,
1733 "video_native_resolution", G_TYPE_UINT, wfd_sink->ini.video_native_resolution,
1734 "video_cea_support", G_TYPE_UINT, CEA_resolution,
1735 "video_vesa_support", G_TYPE_UINT, VESA_resolution,
1736 "video_hh_support", G_TYPE_UINT, HH_resolution,
1737 "video_profile", G_TYPE_UINT, wfd_sink->ini.video_profile,
1738 "video_level", G_TYPE_UINT, wfd_sink->ini.video_level,
1739 "video_latency", G_TYPE_UINT, wfd_sink->ini.video_latency,
1740 "video_vertical_resolution", G_TYPE_INT, wfd_sink->ini.video_vertical_resolution,
1741 "video_horizontal_resolution", G_TYPE_INT, wfd_sink->ini.video_horizontal_resolution,
1742 "video_minimum_slicing", G_TYPE_INT, wfd_sink->ini.video_minimum_slicing,
1743 "video_slice_enc_param", G_TYPE_INT, wfd_sink->ini.video_slice_enc_param,
1744 "video_framerate_control_support", G_TYPE_INT, wfd_sink->ini.video_framerate_control_support,
1747 mm_attrs_get_data_by_name(wfd_sink->attrs, "hdcp_handle", &hdcp_handle);
1748 mm_attrs_get_int_by_name(wfd_sink->attrs, "hdcp_version", &hdcp_version);
1749 mm_attrs_get_int_by_name(wfd_sink->attrs, "hdcp_port", &hdcp_port);
1750 wfd_sink_debug("set hdcp version %d with %d port\n", hdcp_version, hdcp_port);
1752 hdcp_param = gst_structure_new("hdcp_param",
1753 "hdcp_version", G_TYPE_INT, hdcp_version,
1754 "hdcp_port_no", G_TYPE_INT, hdcp_port,
1757 g_object_set(G_OBJECT(wfdrtspsrc), "audio-param", audio_param, NULL);
1758 g_object_set(G_OBJECT(wfdrtspsrc), "video-param", video_param, NULL);
1759 g_object_set(G_OBJECT(wfdrtspsrc), "hdcp-param", hdcp_param, NULL);
1761 g_signal_connect(wfdrtspsrc, "update-media-info",
1762 G_CALLBACK(__mm_wfd_sink_update_stream_info), wfd_sink);
1764 g_signal_connect(wfdrtspsrc, "change-av-format",
1765 G_CALLBACK(__mm_wfd_sink_change_av_format), wfd_sink);
1767 wfd_sink_debug_fleave();
1769 return MM_ERROR_NONE;
1772 static int __mm_wfd_sink_prepare_demux(mm_wfd_sink_t *wfd_sink, GstElement *demux)
1774 wfd_sink_debug_fenter();
1776 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
1777 wfd_sink_return_val_if_fail(demux, MM_ERROR_WFD_NOT_INITIALIZED);
1779 g_signal_connect(demux, "pad-added",
1780 G_CALLBACK(__mm_wfd_sink_demux_pad_added), wfd_sink);
1782 wfd_sink_debug_fleave();
1784 return MM_ERROR_NONE;
1787 static int __mm_wfd_sink_create_pipeline(mm_wfd_sink_t *wfd_sink)
1789 MMWFDSinkGstElement *mainbin = NULL;
1790 GList *element_bucket = NULL;
1794 wfd_sink_debug_fenter();
1796 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
1797 wfd_sink_return_val_if_fail(wfd_sink->attrs, MM_ERROR_WFD_NOT_INITIALIZED);
1799 /* Create pipeline */
1800 wfd_sink->pipeline = (MMWFDSinkGstPipelineInfo *) g_malloc0(sizeof(MMWFDSinkGstPipelineInfo));
1801 if (wfd_sink->pipeline == NULL)
1804 memset(wfd_sink->pipeline, 0, sizeof(MMWFDSinkGstPipelineInfo));
1806 /* create mainbin */
1807 mainbin = (MMWFDSinkGstElement *) g_malloc0(sizeof(MMWFDSinkGstElement) * WFD_SINK_M_NUM);
1808 if (mainbin == NULL)
1811 memset(mainbin, 0, sizeof(MMWFDSinkGstElement) * WFD_SINK_M_NUM);
1813 /* create pipeline */
1814 mainbin[WFD_SINK_M_PIPE].id = WFD_SINK_M_PIPE;
1815 mainbin[WFD_SINK_M_PIPE].gst = gst_pipeline_new("wfdsink");
1816 if (!mainbin[WFD_SINK_M_PIPE].gst) {
1817 wfd_sink_error("failed to create pipeline\n");
1821 /* create wfdrtspsrc */
1822 MMWFDSINK_CREATE_ELEMENT(mainbin, WFD_SINK_M_SRC, "wfdrtspsrc", "wfdsink_source", TRUE);
1823 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_SRC].gst, "src");
1824 if (mainbin[WFD_SINK_M_SRC].gst) {
1825 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_wfdrtspsrc(wfd_sink, mainbin[WFD_SINK_M_SRC].gst)) {
1826 wfd_sink_error("failed to prepare wfdrtspsrc...\n");
1831 /* create rtpmp2tdepay */
1832 MMWFDSINK_CREATE_ELEMENT(mainbin, WFD_SINK_M_DEPAY, "rtpmp2tdepay", "wfdsink_depay", TRUE);
1833 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_DEPAY].gst, "src");
1834 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_DEPAY].gst, "sink");
1836 MMWFDSINK_TS_DATA_DUMP(wfd_sink, mainbin[WFD_SINK_M_DEPAY].gst, "src");
1838 /* create tsdemuxer*/
1839 MMWFDSINK_CREATE_ELEMENT(mainbin, WFD_SINK_M_DEMUX, wfd_sink->ini.name_of_tsdemux, "wfdsink_demux", TRUE);
1840 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_DEMUX].gst, "sink");
1841 if (mainbin[WFD_SINK_M_DEMUX].gst) {
1842 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_demux(wfd_sink, mainbin[WFD_SINK_M_DEMUX].gst)) {
1843 wfd_sink_error("failed to prepare demux...\n");
1848 /* adding created elements to pipeline */
1849 if (!__mm_wfd_sink_gst_element_add_bucket_to_bin(GST_BIN_CAST(mainbin[WFD_SINK_M_PIPE].gst), element_bucket, FALSE)) {
1850 wfd_sink_error("failed to add elements\n");
1854 /* linking elements in the bucket by added order. */
1855 if (__mm_wfd_sink_gst_element_link_bucket(element_bucket) == -1) {
1856 wfd_sink_error("failed to link elements\n");
1860 /* connect bus callback */
1861 bus = gst_pipeline_get_bus(GST_PIPELINE(mainbin[WFD_SINK_M_PIPE].gst));
1863 wfd_sink_error("cannot get bus from pipeline.\n");
1867 /* add bus message callback*/
1868 gst_bus_add_watch(bus, (GstBusFunc)_mm_wfd_sink_msg_callback, wfd_sink);
1870 /* set sync handler to get tag synchronously */
1871 gst_bus_set_sync_handler(bus, _mm_wfd_bus_sync_callback, wfd_sink, NULL);
1873 g_list_free(element_bucket);
1874 gst_object_unref(GST_OBJECT(bus));
1876 /* now we have completed mainbin. take it */
1877 wfd_sink->pipeline->mainbin = mainbin;
1879 wfd_sink_debug_fleave();
1881 return MM_ERROR_NONE;
1885 wfd_sink_error("ERROR : releasing pipeline\n");
1888 g_list_free(element_bucket);
1889 element_bucket = NULL;
1893 gst_object_unref(GST_OBJECT(bus));
1896 /* release element which are not added to bin */
1897 for (i = 1; i < WFD_SINK_M_NUM; i++) { /* NOTE : skip pipeline */
1898 if (mainbin != NULL && mainbin[i].gst) {
1899 GstObject *parent = NULL;
1900 parent = gst_element_get_parent(mainbin[i].gst);
1903 gst_object_unref(GST_OBJECT(mainbin[i].gst));
1904 mainbin[i].gst = NULL;
1906 gst_object_unref(GST_OBJECT(parent));
1911 /* release audiobin with it's childs */
1912 if (mainbin != NULL && mainbin[WFD_SINK_M_PIPE].gst)
1913 gst_object_unref(GST_OBJECT(mainbin[WFD_SINK_M_PIPE].gst));
1915 MMWFDSINK_FREEIF(mainbin);
1917 MMWFDSINK_FREEIF(wfd_sink->pipeline);
1919 return MM_ERROR_WFD_INTERNAL;
1922 int __mm_wfd_sink_link_audiobin(mm_wfd_sink_t *wfd_sink)
1924 MMWFDSinkGstElement *audiobin = NULL;
1925 MMWFDSinkGstElement *first_element = NULL;
1926 MMWFDSinkGstElement *last_element = NULL;
1927 gint audio_codec = MM_WFD_SINK_AUDIO_CODEC_NONE;
1928 GList *element_bucket = NULL;
1929 GstPad *sinkpad = NULL;
1930 GstPad *srcpad = NULL;
1932 wfd_sink_debug_fenter();
1934 wfd_sink_return_val_if_fail(wfd_sink &&
1935 wfd_sink->pipeline &&
1936 wfd_sink->pipeline->audiobin &&
1937 wfd_sink->pipeline->audiobin[WFD_SINK_A_BIN].gst,
1938 MM_ERROR_WFD_NOT_INITIALIZED);
1939 wfd_sink_return_val_if_fail(wfd_sink->prev_audio_dec_src_pad &&
1940 wfd_sink->next_audio_dec_sink_pad,
1941 MM_ERROR_WFD_INTERNAL);
1943 if (wfd_sink->audio_bin_is_linked) {
1944 wfd_sink_debug("audiobin is already linked... nothing to do\n");
1945 return MM_ERROR_NONE;
1949 audiobin = wfd_sink->pipeline->audiobin;
1951 /* check audio codec */
1952 audio_codec = wfd_sink->stream_info.audio_stream_info.codec;
1953 switch (audio_codec) {
1954 case MM_WFD_SINK_AUDIO_CODEC_LPCM:
1955 if (audiobin[WFD_SINK_A_LPCM_CONVERTER].gst)
1956 element_bucket = g_list_append(element_bucket, &audiobin[WFD_SINK_A_LPCM_CONVERTER]);
1957 if (audiobin[WFD_SINK_A_LPCM_FILTER].gst)
1958 element_bucket = g_list_append(element_bucket, &audiobin[WFD_SINK_A_LPCM_FILTER]);
1961 case MM_WFD_SINK_AUDIO_CODEC_AAC:
1962 if (audiobin[WFD_SINK_A_AAC_PARSE].gst)
1963 element_bucket = g_list_append(element_bucket, &audiobin[WFD_SINK_A_AAC_PARSE]);
1964 if (audiobin[WFD_SINK_A_AAC_DEC].gst)
1965 element_bucket = g_list_append(element_bucket, &audiobin[WFD_SINK_A_AAC_DEC]);
1968 case MM_WFD_SINK_AUDIO_CODEC_AC3:
1969 if (audiobin[WFD_SINK_A_AC3_PARSE].gst)
1970 element_bucket = g_list_append(element_bucket, &audiobin[WFD_SINK_A_AC3_PARSE]);
1971 if (audiobin[WFD_SINK_A_AC3_DEC].gst)
1972 element_bucket = g_list_append(element_bucket, &audiobin[WFD_SINK_A_AC3_DEC]);
1976 wfd_sink_error("audio type is not decied yet. cannot link audio decoder...\n");
1977 return MM_ERROR_WFD_INTERNAL;
1981 if (!element_bucket) {
1982 wfd_sink_debug("there is no additional elements to be linked... just link audiobin.\n");
1983 if (GST_PAD_LINK_OK != gst_pad_link_full(wfd_sink->prev_audio_dec_src_pad, wfd_sink->next_audio_dec_sink_pad, GST_PAD_LINK_CHECK_NOTHING)) {
1984 wfd_sink_error("failed to link audiobin....\n");
1990 /* adding elements to audiobin */
1991 if (!__mm_wfd_sink_gst_element_add_bucket_to_bin(GST_BIN_CAST(audiobin[WFD_SINK_A_BIN].gst), element_bucket, FALSE)) {
1992 wfd_sink_error("failed to add elements to audiobin\n");
1996 /* linking elements in the bucket by added order. */
1997 if (__mm_wfd_sink_gst_element_link_bucket(element_bucket) == -1) {
1998 wfd_sink_error("failed to link elements\n");
2003 first_element = (MMWFDSinkGstElement *)g_list_nth_data(element_bucket, 0);
2004 if (!first_element) {
2005 wfd_sink_error("failed to get first element to be linked....\n");
2009 sinkpad = gst_element_get_static_pad(GST_ELEMENT(first_element->gst), "sink");
2011 wfd_sink_error("failed to get sink pad from element(%s)\n", GST_ELEMENT_NAME(first_element->gst));
2015 if (GST_PAD_LINK_OK != gst_pad_link_full(wfd_sink->prev_audio_dec_src_pad, sinkpad, GST_PAD_LINK_CHECK_NOTHING)) {
2016 wfd_sink_error("failed to link audiobin....\n");
2020 gst_object_unref(GST_OBJECT(sinkpad));
2025 last_element = (MMWFDSinkGstElement *)g_list_nth_data(element_bucket, g_list_length(element_bucket) - 1);
2026 if (!last_element) {
2027 wfd_sink_error("failed to get last element to be linked....\n");
2031 srcpad = gst_element_get_static_pad(GST_ELEMENT(last_element->gst), "src");
2033 wfd_sink_error("failed to get src pad from element(%s)\n", GST_ELEMENT_NAME(last_element->gst));
2037 if (GST_PAD_LINK_OK != gst_pad_link_full(srcpad, wfd_sink->next_audio_dec_sink_pad, GST_PAD_LINK_CHECK_NOTHING)) {
2038 wfd_sink_error("failed to link audiobin....\n");
2042 gst_object_unref(GST_OBJECT(srcpad));
2045 g_list_free(element_bucket);
2048 wfd_sink->audio_bin_is_linked = TRUE;
2050 wfd_sink_debug_fleave();
2052 return MM_ERROR_NONE;
2057 gst_object_unref(GST_OBJECT(srcpad));
2061 gst_object_unref(GST_OBJECT(sinkpad));
2064 g_list_free(element_bucket);
2066 return MM_ERROR_WFD_INTERNAL;
2069 static int __mm_wfd_sink_prepare_audiosink(mm_wfd_sink_t *wfd_sink, GstElement *audio_sink)
2071 wfd_sink_debug_fenter();
2073 /* check audiosink is created */
2074 wfd_sink_return_val_if_fail(audio_sink, MM_ERROR_WFD_INVALID_ARGUMENT);
2075 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
2077 g_object_set(G_OBJECT(audio_sink), "provide-clock", FALSE, NULL);
2078 g_object_set(G_OBJECT(audio_sink), "buffer-time", 100000LL, NULL);
2079 g_object_set(G_OBJECT(audio_sink), "slave-method", 2, NULL);
2080 g_object_set(G_OBJECT(audio_sink), "async", wfd_sink->ini.audio_sink_async, NULL);
2081 g_object_set(G_OBJECT(audio_sink), "ts-offset", (gint64)wfd_sink->ini.sink_ts_offset, NULL);
2083 wfd_sink_debug_fleave();
2085 return MM_ERROR_NONE;
2089 __mm_wfd_sink_queue_overrun(GstElement *element, gpointer u_data)
2093 return_if_fail(element);
2095 wfd_sink_warning("%s is overrun\n",
2096 GST_STR_NULL(GST_ELEMENT_NAME(element)));
2103 static int __mm_wfd_sink_destroy_audiobin(mm_wfd_sink_t *wfd_sink)
2105 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
2106 MMWFDSinkGstElement *audiobin = NULL;
2107 GstObject *parent = NULL;
2110 wfd_sink_debug_fenter();
2112 wfd_sink_return_val_if_fail(wfd_sink,
2113 MM_ERROR_WFD_NOT_INITIALIZED);
2115 if (wfd_sink->pipeline &&
2116 wfd_sink->pipeline->audiobin &&
2117 wfd_sink->pipeline->audiobin[WFD_SINK_A_BIN].gst) {
2118 audiobin = wfd_sink->pipeline->audiobin;
2120 wfd_sink_debug("audiobin is not created, nothing to destroy\n");
2121 return MM_ERROR_NONE;
2125 parent = gst_element_get_parent(audiobin[WFD_SINK_A_BIN].gst);
2127 wfd_sink_debug("audiobin has no parent.. need to relase by itself\n");
2129 if (GST_STATE(audiobin[WFD_SINK_A_BIN].gst) >= GST_STATE_READY) {
2130 wfd_sink_debug("try to change state of audiobin to NULL\n");
2131 ret = gst_element_set_state(audiobin[WFD_SINK_A_BIN].gst, GST_STATE_NULL);
2132 if (ret != GST_STATE_CHANGE_SUCCESS) {
2133 wfd_sink_error("failed to change state of audiobin to NULL\n");
2134 return MM_ERROR_WFD_INTERNAL;
2138 /* release element which are not added to bin */
2139 for (i = 1; i < WFD_SINK_A_NUM; i++) { /* NOTE : skip bin */
2140 if (audiobin[i].gst) {
2141 parent = gst_element_get_parent(audiobin[i].gst);
2143 wfd_sink_debug("unref %s(current ref %d)\n",
2144 GST_STR_NULL(GST_ELEMENT_NAME(audiobin[i].gst)),
2145 ((GObject *) audiobin[i].gst)->ref_count);
2146 gst_object_unref(GST_OBJECT(audiobin[i].gst));
2147 audiobin[i].gst = NULL;
2149 wfd_sink_debug("unref %s(current ref %d)\n",
2150 GST_STR_NULL(GST_ELEMENT_NAME(audiobin[i].gst)),
2151 ((GObject *) audiobin[i].gst)->ref_count);
2152 gst_object_unref(GST_OBJECT(parent));
2157 /* release audiobin with it's childs */
2158 if (audiobin[WFD_SINK_A_BIN].gst)
2159 gst_object_unref(GST_OBJECT(audiobin[WFD_SINK_A_BIN].gst));
2162 wfd_sink_debug("audiobin has parent(%s), unref it \n",
2163 GST_STR_NULL(GST_OBJECT_NAME(GST_OBJECT(parent))));
2165 gst_object_unref(GST_OBJECT(parent));
2168 wfd_sink_debug_fleave();
2170 return MM_ERROR_NONE;
2173 static int __mm_wfd_sink_create_audiobin(mm_wfd_sink_t *wfd_sink)
2175 MMWFDSinkGstElement *audiobin = NULL;
2176 gint audio_codec = MM_WFD_SINK_AUDIO_CODEC_NONE;
2177 gboolean link_audio_dec = TRUE;
2178 GList *element_bucket = NULL;
2180 GstPad *ghostpad = NULL;
2181 GstCaps *caps = NULL;
2183 wfd_sink_debug_fenter();
2185 wfd_sink_return_val_if_fail(wfd_sink &&
2186 wfd_sink->pipeline &&
2187 wfd_sink->pipeline->mainbin,
2188 MM_ERROR_WFD_NOT_INITIALIZED);
2191 audiobin = (MMWFDSinkGstElement *)g_malloc0(sizeof(MMWFDSinkGstElement) * WFD_SINK_A_NUM);
2193 wfd_sink_error("failed to allocate memory for audiobin\n");
2194 return MM_ERROR_WFD_NO_FREE_SPACE;
2197 /* create audiobin */
2198 audiobin[WFD_SINK_A_BIN].id = WFD_SINK_A_BIN;
2199 audiobin[WFD_SINK_A_BIN].gst = gst_bin_new("audiobin");
2200 if (!audiobin[WFD_SINK_A_BIN].gst) {
2201 wfd_sink_error("failed to create audiobin\n");
2205 /* check audio decoder could be linked or not */
2206 switch (wfd_sink->stream_info.audio_stream_info.codec) {
2207 case MM_WFD_SINK_AUDIO_CODEC_AAC:
2208 audio_codec = WFD_AUDIO_AAC;
2210 case MM_WFD_SINK_AUDIO_CODEC_AC3:
2211 audio_codec = WFD_AUDIO_AC3;
2213 case MM_WFD_SINK_AUDIO_CODEC_LPCM:
2214 audio_codec = WFD_AUDIO_LPCM;
2216 case MM_WFD_SINK_AUDIO_CODEC_NONE:
2218 wfd_sink_debug("audio decoder could NOT be linked now, just prepare.\n");
2219 audio_codec = wfd_sink->ini.audio_codec;
2220 link_audio_dec = FALSE;
2224 /* set need to link audio decoder flag*/
2225 wfd_sink->audio_bin_is_linked = link_audio_dec;
2227 /* queue - drm - parse - dec/capsfilter - audioconvert- volume - sink */
2229 MMWFDSINK_CREATE_ELEMENT(audiobin, WFD_SINK_A_QUEUE, "queue", "audio_queue", link_audio_dec);
2230 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_QUEUE].gst, "sink");
2231 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_QUEUE].gst, "src");
2232 if (audiobin[WFD_SINK_A_QUEUE].gst) {
2233 g_object_set(G_OBJECT(audiobin[WFD_SINK_A_QUEUE].gst), "max-size-bytes", 0, NULL);
2234 g_object_set(G_OBJECT(audiobin[WFD_SINK_A_QUEUE].gst), "max-size-buffers", 0, NULL);
2235 g_object_set(G_OBJECT(audiobin[WFD_SINK_A_QUEUE].gst), "max-size-time", (guint64)3000000000ULL, NULL);
2236 g_signal_connect(audiobin[WFD_SINK_A_QUEUE].gst, "overrun",
2237 G_CALLBACK(__mm_wfd_sink_queue_overrun), wfd_sink);
2239 if (!link_audio_dec) {
2240 if (!gst_bin_add(GST_BIN_CAST(audiobin[WFD_SINK_A_BIN].gst), audiobin[WFD_SINK_A_QUEUE].gst)) {
2241 wfd_sink_error("failed to add %s to audiobin\n",
2242 GST_STR_NULL(GST_ELEMENT_NAME(audiobin[WFD_SINK_A_QUEUE].gst)));
2246 if (audiobin[WFD_SINK_A_HDCP].gst) {
2247 if (!gst_bin_add(GST_BIN_CAST(audiobin[WFD_SINK_A_BIN].gst), audiobin[WFD_SINK_A_HDCP].gst)) {
2248 wfd_sink_error("failed to add %s to audiobin\n",
2249 GST_STR_NULL(GST_ELEMENT_NAME(audiobin[WFD_SINK_A_HDCP].gst)));
2253 if (!gst_element_link(audiobin[WFD_SINK_A_QUEUE].gst, audiobin[WFD_SINK_A_HDCP].gst)) {
2254 wfd_sink_error("failed to link [%s] to [%s] success\n",
2255 GST_STR_NULL(GST_ELEMENT_NAME(audiobin[WFD_SINK_A_QUEUE].gst)),
2256 GST_STR_NULL(GST_ELEMENT_NAME(audiobin[WFD_SINK_A_HDCP].gst)));
2260 wfd_sink->prev_audio_dec_src_pad = gst_element_get_static_pad(audiobin[WFD_SINK_A_HDCP].gst, "src");
2262 wfd_sink->prev_audio_dec_src_pad = gst_element_get_static_pad(audiobin[WFD_SINK_A_QUEUE].gst, "src");
2265 if (!wfd_sink->prev_audio_dec_src_pad) {
2266 wfd_sink_error("failed to get src pad from previous element of audio decoder\n");
2270 wfd_sink_debug("take src pad from previous element of audio decoder for linking\n");
2273 if (audio_codec & WFD_AUDIO_LPCM) {
2274 /* create LPCM converter */
2275 MMWFDSINK_CREATE_ELEMENT(audiobin, WFD_SINK_A_LPCM_CONVERTER, wfd_sink->ini.name_of_lpcm_converter, "audio_lpcm_convert", link_audio_dec);
2276 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_LPCM_CONVERTER].gst, "sink");
2277 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_LPCM_CONVERTER].gst, "src");
2279 /* create LPCM filter */
2280 MMWFDSINK_CREATE_ELEMENT(audiobin, WFD_SINK_A_LPCM_FILTER, wfd_sink->ini.name_of_lpcm_filter, "audio_lpcm_filter", link_audio_dec);
2281 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_LPCM_FILTER].gst, "sink");
2282 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_LPCM_FILTER].gst, "src");
2283 if (audiobin[WFD_SINK_A_LPCM_FILTER].gst) {
2284 caps = gst_caps_new_simple("audio/x-raw",
2285 "rate", G_TYPE_INT, 48000,
2286 "channels", G_TYPE_INT, 2,
2287 "format", G_TYPE_STRING, "S16BE", NULL);
2289 g_object_set(G_OBJECT(audiobin[WFD_SINK_A_LPCM_FILTER].gst), "caps", caps, NULL);
2290 gst_object_unref(GST_OBJECT(caps));
2294 if (audio_codec & WFD_AUDIO_AAC) {
2295 /* create AAC parse */
2296 MMWFDSINK_CREATE_ELEMENT(audiobin, WFD_SINK_A_AAC_PARSE, wfd_sink->ini.name_of_aac_parser, "audio_aac_parser", link_audio_dec);
2297 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_AAC_PARSE].gst, "sink");
2298 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_AAC_PARSE].gst, "src");
2300 /* create AAC decoder */
2301 MMWFDSINK_CREATE_ELEMENT(audiobin, WFD_SINK_A_AAC_DEC, wfd_sink->ini.name_of_aac_decoder, "audio_aac_dec", link_audio_dec);
2302 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_AAC_DEC].gst, "sink");
2303 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_AAC_DEC].gst, "src");
2306 if (audio_codec & WFD_AUDIO_AC3) {
2307 /* create AC3 parser */
2308 MMWFDSINK_CREATE_ELEMENT(audiobin, WFD_SINK_A_AC3_PARSE, wfd_sink->ini.name_of_ac3_parser, "audio_ac3_parser", link_audio_dec);
2309 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_AC3_PARSE].gst, "sink");
2310 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_AC3_PARSE].gst, "src");
2312 /* create AC3 decoder */
2313 MMWFDSINK_CREATE_ELEMENT(audiobin, WFD_SINK_A_AC3_DEC, wfd_sink->ini.name_of_ac3_decoder, "audio_ac3_dec", link_audio_dec);
2314 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_AC3_DEC].gst, "sink");
2315 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_AC3_DEC].gst, "src");
2318 /* create resampler */
2319 MMWFDSINK_CREATE_ELEMENT(audiobin, WFD_SINK_A_RESAMPLER, wfd_sink->ini.name_of_audio_resampler, "audio_resampler", TRUE);
2320 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_RESAMPLER].gst, "sink");
2321 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_RESAMPLER].gst, "src");
2324 MMWFDSINK_CREATE_ELEMENT(audiobin, WFD_SINK_A_VOLUME, wfd_sink->ini.name_of_audio_volume, "audio_volume", TRUE);
2325 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_VOLUME].gst, "sink");
2326 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_VOLUME].gst, "src");
2328 /*TODO gstreamer-1.0 alsasink does not want process not S16LE format. */
2329 MMWFDSINK_CREATE_ELEMENT(audiobin, WFD_SINK_A_CAPSFILTER, "capsfilter", "audio_capsfilter", TRUE);
2330 if (audiobin[WFD_SINK_A_CAPSFILTER].gst) {
2331 caps = gst_caps_from_string("audio/x-raw, format=(string)S16LE");
2332 g_object_set(G_OBJECT(audiobin[WFD_SINK_A_CAPSFILTER].gst), "caps", caps, NULL);
2333 gst_caps_unref(caps);
2337 MMWFDSINK_CREATE_ELEMENT(audiobin, WFD_SINK_A_SINK, wfd_sink->ini.name_of_audio_sink, "audio_sink", TRUE);
2338 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_SINK].gst, "sink");
2339 if (audiobin[WFD_SINK_A_SINK].gst) {
2340 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_audiosink(wfd_sink, audiobin[WFD_SINK_A_SINK].gst)) {
2341 wfd_sink_error("failed to set audio sink property....\n");
2346 if (!link_audio_dec) {
2347 MMWFDSinkGstElement *first_element = NULL;
2349 first_element = (MMWFDSinkGstElement *)g_list_nth_data(element_bucket, 0);
2350 if (!first_element) {
2351 wfd_sink_error("failed to get first element\n");
2355 wfd_sink->next_audio_dec_sink_pad = gst_element_get_static_pad(GST_ELEMENT(first_element->gst), "sink");
2356 if (!wfd_sink->next_audio_dec_sink_pad) {
2357 wfd_sink_error("failed to get sink pad from next element of audio decoder\n");
2361 wfd_sink_debug("take sink pad from next element of audio decoder for linking\n");
2364 /* adding created elements to audiobin */
2365 if (!__mm_wfd_sink_gst_element_add_bucket_to_bin(GST_BIN_CAST(audiobin[WFD_SINK_A_BIN].gst), element_bucket, FALSE)) {
2366 wfd_sink_error("failed to add elements\n");
2370 /* linking elements in the bucket by added order. */
2371 if (__mm_wfd_sink_gst_element_link_bucket(element_bucket) == -1) {
2372 wfd_sink_error("failed to link elements\n");
2376 /* get queue's sinkpad for creating ghostpad */
2377 pad = gst_element_get_static_pad(audiobin[WFD_SINK_A_QUEUE].gst, "sink");
2379 wfd_sink_error("failed to get pad from queue of audiobin\n");
2383 ghostpad = gst_ghost_pad_new("sink", pad);
2385 wfd_sink_error("failed to create ghostpad\n");
2389 if (FALSE == gst_element_add_pad(audiobin[WFD_SINK_A_BIN].gst, ghostpad)) {
2390 wfd_sink_error("failed to add ghostpad to audiobin\n");
2394 gst_object_unref(GST_OBJECT(pad));
2396 g_list_free(element_bucket);
2399 wfd_sink->pipeline->audiobin = audiobin;
2401 wfd_sink_debug_fleave();
2403 return MM_ERROR_NONE;
2406 wfd_sink_error("failed to create audiobin, releasing all\n");
2408 if (wfd_sink->next_audio_dec_sink_pad)
2409 gst_object_unref(GST_OBJECT(wfd_sink->next_audio_dec_sink_pad));
2410 wfd_sink->next_audio_dec_sink_pad = NULL;
2412 if (wfd_sink->prev_audio_dec_src_pad)
2413 gst_object_unref(GST_OBJECT(wfd_sink->prev_audio_dec_src_pad));
2414 wfd_sink->prev_audio_dec_src_pad = NULL;
2417 gst_object_unref(GST_OBJECT(pad));
2421 gst_object_unref(GST_OBJECT(ghostpad));
2425 g_list_free(element_bucket);
2426 element_bucket = NULL;
2428 /* release element which are not added to bin */
2429 __mm_wfd_sink_destroy_audiobin(wfd_sink);
2431 MMWFDSINK_FREEIF(audiobin);
2433 return MM_ERROR_WFD_INTERNAL;
2436 int __mm_wfd_sink_link_videobin(mm_wfd_sink_t *wfd_sink)
2438 MMWFDSinkGstElement *videobin = NULL;
2440 wfd_sink_debug_fenter();
2442 wfd_sink_return_val_if_fail(wfd_sink &&
2443 wfd_sink->pipeline &&
2444 wfd_sink->pipeline->videobin &&
2445 wfd_sink->pipeline->videobin[WFD_SINK_V_BIN].gst,
2446 MM_ERROR_WFD_NOT_INITIALIZED);
2448 if (wfd_sink->video_bin_is_linked) {
2449 wfd_sink_debug("videobin is already linked... nothing to do\n");
2450 return MM_ERROR_NONE;
2454 videobin = wfd_sink->pipeline->videobin;
2456 if (videobin[WFD_SINK_V_CAPSSETTER].gst) {
2457 GstCaps *caps = NULL;
2458 caps = gst_caps_new_simple("video/x-h264",
2459 "width", G_TYPE_INT, wfd_sink->stream_info.video_stream_info.width,
2460 "height", G_TYPE_INT, wfd_sink->stream_info.video_stream_info.height,
2461 "framerate", GST_TYPE_FRACTION, wfd_sink->stream_info.video_stream_info.frame_rate, 1, NULL);
2462 g_object_set(G_OBJECT(videobin[WFD_SINK_V_CAPSSETTER].gst), "caps", caps, NULL);
2463 gst_object_unref(GST_OBJECT(caps));
2466 wfd_sink->video_bin_is_linked = TRUE;
2468 wfd_sink_debug_fleave();
2470 return MM_ERROR_NONE;
2473 static int __mm_wfd_sink_prepare_videodec(mm_wfd_sink_t *wfd_sink, GstElement *video_dec)
2475 wfd_sink_debug_fenter();
2477 /* check video decoder is created */
2478 wfd_sink_return_val_if_fail(video_dec, MM_ERROR_WFD_INVALID_ARGUMENT);
2479 wfd_sink_return_val_if_fail(wfd_sink && wfd_sink->attrs, MM_ERROR_WFD_NOT_INITIALIZED);
2481 wfd_sink_debug_fleave();
2483 return MM_ERROR_NONE;
2486 static int __mm_wfd_sink_prepare_videosink(mm_wfd_sink_t *wfd_sink, GstElement *video_sink)
2488 gboolean visible = TRUE;
2489 gint surface_type = MM_DISPLAY_SURFACE_X;
2492 wfd_sink_debug_fenter();
2494 /* check videosink is created */
2495 wfd_sink_return_val_if_fail(video_sink, MM_ERROR_WFD_INVALID_ARGUMENT);
2496 wfd_sink_return_val_if_fail(wfd_sink && wfd_sink->attrs, MM_ERROR_WFD_NOT_INITIALIZED);
2498 /* update display surface */
2499 /* mm_attrs_get_int_by_name(wfd_sink->attrs, "display_surface_type", &surface_type); */
2500 wfd_sink_debug("check display surface type attribute: %d", surface_type);
2501 mm_attrs_get_int_by_name(wfd_sink->attrs, "display_visible", &visible);
2502 wfd_sink_debug("check display visible attribute: %d", visible);
2504 /* configuring display */
2505 switch (surface_type) {
2506 case MM_DISPLAY_SURFACE_EVAS: {
2507 void *object = NULL;
2510 /* common case if using evas surface */
2511 mm_attrs_get_data_by_name(wfd_sink->attrs, "display_overlay", &object);
2512 mm_attrs_get_int_by_name(wfd_sink->attrs, "display_evas_do_scaling", &scaling);
2514 wfd_sink_debug("set video param : evas-object %x", object);
2515 g_object_set(G_OBJECT(video_sink), "evas-object", object, NULL);
2517 wfd_sink_error("no evas object");
2518 return MM_ERROR_WFD_INTERNAL;
2523 case MM_DISPLAY_SURFACE_X: {
2527 mm_attrs_get_data_by_name(wfd_sink->attrs, "display_overlay", (void **)&object);
2530 wfd_sink_debug("xid = %lu", xid);
2531 gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(video_sink), xid);
2533 wfd_sink_warning("Handle is NULL. Set xid as 0.. but, it's not recommended.");
2534 gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(video_sink), 0);
2539 case MM_DISPLAY_SURFACE_NULL: {
2541 wfd_sink_error("Not Supported Surface.");
2542 return MM_ERROR_WFD_INTERNAL;
2546 wfd_sink_error("Not Supported Surface.(default case)");
2547 return MM_ERROR_WFD_INTERNAL;
2552 g_object_set(G_OBJECT(video_sink), "qos", FALSE, NULL);
2553 g_object_set(G_OBJECT(video_sink), "async", wfd_sink->ini.video_sink_async, NULL);
2554 g_object_set(G_OBJECT(video_sink), "max-lateness", (gint64)wfd_sink->ini.video_sink_max_lateness, NULL);
2555 g_object_set(G_OBJECT(video_sink), "visible", visible, NULL);
2556 g_object_set(G_OBJECT(video_sink), "ts-offset", (gint64)(wfd_sink->ini.sink_ts_offset), NULL);
2558 wfd_sink_debug_fleave();
2560 return MM_ERROR_NONE;
2563 static int __mm_wfd_sink_destroy_videobin(mm_wfd_sink_t *wfd_sink)
2565 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
2566 MMWFDSinkGstElement *videobin = NULL;
2567 GstObject *parent = NULL;
2570 wfd_sink_debug_fenter();
2572 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
2574 if (wfd_sink->pipeline &&
2575 wfd_sink->pipeline->videobin &&
2576 wfd_sink->pipeline->videobin[WFD_SINK_V_BIN].gst) {
2577 videobin = wfd_sink->pipeline->videobin;
2579 wfd_sink_debug("videobin is not created, nothing to destroy\n");
2580 return MM_ERROR_NONE;
2584 parent = gst_element_get_parent(videobin[WFD_SINK_V_BIN].gst);
2586 wfd_sink_debug("videobin has no parent.. need to relase by itself\n");
2588 if (GST_STATE(videobin[WFD_SINK_V_BIN].gst) >= GST_STATE_READY) {
2589 wfd_sink_debug("try to change state of videobin to NULL\n");
2590 ret = gst_element_set_state(videobin[WFD_SINK_V_BIN].gst, GST_STATE_NULL);
2591 if (ret != GST_STATE_CHANGE_SUCCESS) {
2592 wfd_sink_error("failed to change state of videobin to NULL\n");
2593 return MM_ERROR_WFD_INTERNAL;
2596 /* release element which are not added to bin */
2597 for (i = 1; i < WFD_SINK_V_NUM; i++) { /* NOTE : skip bin */
2598 if (videobin[i].gst) {
2599 parent = gst_element_get_parent(videobin[i].gst);
2601 wfd_sink_debug("unref %s(current ref %d)\n",
2602 GST_STR_NULL(GST_ELEMENT_NAME(videobin[i].gst)),
2603 ((GObject *) videobin[i].gst)->ref_count);
2604 gst_object_unref(GST_OBJECT(videobin[i].gst));
2605 videobin[i].gst = NULL;
2607 wfd_sink_debug("unref %s(current ref %d)\n",
2608 GST_STR_NULL(GST_ELEMENT_NAME(videobin[i].gst)),
2609 ((GObject *) videobin[i].gst)->ref_count);
2610 gst_object_unref(GST_OBJECT(parent));
2614 /* release audiobin with it's childs */
2615 if (videobin[WFD_SINK_V_BIN].gst) {
2616 gst_object_unref(GST_OBJECT(videobin[WFD_SINK_V_BIN].gst));
2619 wfd_sink_debug("videobin has parent(%s), unref it \n",
2620 GST_STR_NULL(GST_OBJECT_NAME(GST_OBJECT(parent))));
2622 gst_object_unref(GST_OBJECT(parent));
2625 wfd_sink_debug_fleave();
2627 return MM_ERROR_NONE;
2631 static int __mm_wfd_sink_create_videobin(mm_wfd_sink_t *wfd_sink)
2633 MMWFDSinkGstElement *first_element = NULL;
2634 MMWFDSinkGstElement *videobin = NULL;
2635 GList *element_bucket = NULL;
2637 GstPad *ghostpad = NULL;
2639 wfd_sink_debug_fenter();
2641 wfd_sink_return_val_if_fail(wfd_sink &&
2642 wfd_sink->pipeline &&
2643 wfd_sink->pipeline->mainbin,
2644 MM_ERROR_WFD_NOT_INITIALIZED);
2647 videobin = (MMWFDSinkGstElement *)g_malloc0(sizeof(MMWFDSinkGstElement) * WFD_SINK_V_NUM);
2649 wfd_sink_error("failed to allocate memory for videobin\n");
2650 return MM_ERROR_WFD_NO_FREE_SPACE;
2653 /* create videobin */
2654 videobin[WFD_SINK_V_BIN].id = WFD_SINK_V_BIN;
2655 videobin[WFD_SINK_V_BIN].gst = gst_bin_new("videobin");
2656 if (!videobin[WFD_SINK_V_BIN].gst) {
2657 wfd_sink_error("failed to create videobin\n");
2661 /* queue - drm - parse - dec - sink */
2663 MMWFDSINK_CREATE_ELEMENT(videobin, WFD_SINK_V_QUEUE, "queue", "video_queue", TRUE);
2664 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, videobin[WFD_SINK_V_QUEUE].gst, "sink");
2665 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, videobin[WFD_SINK_V_QUEUE].gst, "src");
2666 if (videobin[WFD_SINK_V_QUEUE].gst) {
2667 g_object_set(G_OBJECT(videobin[WFD_SINK_V_QUEUE].gst), "max-size-bytes", 0, NULL);
2668 g_object_set(G_OBJECT(videobin[WFD_SINK_V_QUEUE].gst), "max-size-buffers", 0, NULL);
2669 g_object_set(G_OBJECT(videobin[WFD_SINK_V_QUEUE].gst), "max-size-time", (guint64)3000000000ULL, NULL);
2670 g_signal_connect(videobin[WFD_SINK_V_QUEUE].gst, "overrun",
2671 G_CALLBACK(__mm_wfd_sink_queue_overrun), wfd_sink);
2675 MMWFDSINK_CREATE_ELEMENT(videobin, WFD_SINK_V_PARSE, wfd_sink->ini.name_of_video_parser, "video_parser", TRUE);
2676 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, videobin[WFD_SINK_V_PARSE].gst, "sink");
2677 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, videobin[WFD_SINK_V_PARSE].gst, "src");
2679 /* create capssetter */
2680 MMWFDSINK_CREATE_ELEMENT(videobin, WFD_SINK_V_CAPSSETTER, wfd_sink->ini.name_of_video_capssetter, "video_capssetter", TRUE);
2681 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, videobin[WFD_SINK_V_CAPSSETTER].gst, "sink");
2682 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, videobin[WFD_SINK_V_CAPSSETTER].gst, "src");
2685 MMWFDSINK_CREATE_ELEMENT(videobin, WFD_SINK_V_DEC, wfd_sink->ini.name_of_video_decoder, "video_dec", TRUE);
2686 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, videobin[WFD_SINK_V_DEC].gst, "sink");
2687 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, videobin[WFD_SINK_V_DEC].gst, "src");
2688 if (videobin[WFD_SINK_V_DEC].gst) {
2689 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_videodec(wfd_sink, videobin[WFD_SINK_V_DEC].gst)) {
2690 wfd_sink_error("failed to set video sink property....\n");
2695 /* create convert */
2696 MMWFDSINK_CREATE_ELEMENT(videobin, WFD_SINK_V_CONVERT, wfd_sink->ini.name_of_video_converter, "video_convert", TRUE);
2697 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, videobin[WFD_SINK_V_CONVERT].gst, "sink");
2698 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, videobin[WFD_SINK_V_CONVERT].gst, "src");
2701 MMWFDSINK_CREATE_ELEMENT(videobin, WFD_SINK_V_CAPSFILTER, wfd_sink->ini.name_of_video_filter, "video_filter", TRUE);
2702 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, videobin[WFD_SINK_V_CAPSFILTER].gst, "sink");
2703 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, videobin[WFD_SINK_V_CAPSFILTER].gst, "src");
2704 if (videobin[WFD_SINK_V_CAPSFILTER].gst) {
2705 GstCaps *caps = NULL;
2706 caps = gst_caps_new_simple("video/x-raw",
2707 "format", G_TYPE_STRING, "SN12", NULL);
2708 g_object_set(G_OBJECT(videobin[WFD_SINK_V_CAPSFILTER].gst), "caps", caps, NULL);
2709 gst_object_unref(GST_OBJECT(caps));
2714 MMWFDSINK_CREATE_ELEMENT(videobin, WFD_SINK_V_SINK, wfd_sink->ini.name_of_video_sink, "video_sink", TRUE);
2715 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, videobin[WFD_SINK_V_SINK].gst, "sink");
2716 if (videobin[WFD_SINK_V_SINK].gst) {
2717 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_videosink(wfd_sink, videobin[WFD_SINK_V_SINK].gst)) {
2718 wfd_sink_error("failed to set video sink property....\n");
2723 /* adding created elements to videobin */
2724 if (!__mm_wfd_sink_gst_element_add_bucket_to_bin(GST_BIN_CAST(videobin[WFD_SINK_V_BIN].gst), element_bucket, FALSE)) {
2725 wfd_sink_error("failed to add elements\n");
2729 /* linking elements in the bucket by added order. */
2730 if (__mm_wfd_sink_gst_element_link_bucket(element_bucket) == -1) {
2731 wfd_sink_error("failed to link elements\n");
2735 /* get first element's sinkpad for creating ghostpad */
2736 first_element = (MMWFDSinkGstElement *)g_list_nth_data(element_bucket, 0);
2737 if (!first_element) {
2738 wfd_sink_error("failed to get first element of videobin\n");
2742 pad = gst_element_get_static_pad(GST_ELEMENT(first_element->gst), "sink");
2744 wfd_sink_error("failed to get pad from first element(%s) of videobin\n", GST_ELEMENT_NAME(first_element->gst));
2748 ghostpad = gst_ghost_pad_new("sink", pad);
2750 wfd_sink_error("failed to create ghostpad\n");
2754 if (FALSE == gst_element_add_pad(videobin[WFD_SINK_V_BIN].gst, ghostpad)) {
2755 wfd_sink_error("failed to add ghostpad to videobin\n");
2759 gst_object_unref(GST_OBJECT(pad));
2761 g_list_free(element_bucket);
2765 wfd_sink->pipeline->videobin = videobin;
2767 if (wfd_sink->ini.video_sink_async) {
2769 bus = gst_element_get_bus(videobin[WFD_SINK_V_BIN].gst);
2771 gst_bus_set_sync_handler(bus, _mm_wfd_bus_sync_callback, wfd_sink, NULL);
2772 gst_object_unref(bus);
2775 wfd_sink_debug_fleave();
2777 return MM_ERROR_NONE;
2781 wfd_sink_error("failed to create videobin, releasing all\n");
2784 gst_object_unref(GST_OBJECT(pad));
2788 gst_object_unref(GST_OBJECT(ghostpad));
2791 g_list_free(element_bucket);
2793 __mm_wfd_sink_destroy_videobin(wfd_sink);
2795 MMWFDSINK_FREEIF(videobin);
2797 return MM_ERROR_WFD_INTERNAL;
2800 static int __mm_wfd_sink_destroy_pipeline(mm_wfd_sink_t *wfd_sink)
2802 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
2804 wfd_sink_debug_fenter();
2806 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
2808 if (wfd_sink->prev_audio_dec_src_pad)
2809 gst_object_unref(GST_OBJECT(wfd_sink->prev_audio_dec_src_pad));
2810 wfd_sink->prev_audio_dec_src_pad = NULL;
2812 if (wfd_sink->next_audio_dec_sink_pad)
2813 gst_object_unref(GST_OBJECT(wfd_sink->next_audio_dec_sink_pad));
2814 wfd_sink->next_audio_dec_sink_pad = NULL;
2816 /* cleanup gst stuffs */
2817 if (wfd_sink->pipeline) {
2818 MMWFDSinkGstElement *mainbin = wfd_sink->pipeline->mainbin;
2821 MMWFDSinkGstElement *audiobin = wfd_sink->pipeline->audiobin;
2822 MMWFDSinkGstElement *videobin = wfd_sink->pipeline->videobin;
2824 ret = gst_element_set_state(mainbin[WFD_SINK_M_PIPE].gst, GST_STATE_NULL);
2825 if (ret != GST_STATE_CHANGE_SUCCESS) {
2826 wfd_sink_error("failed to change state of mainbin to NULL\n");
2827 return MM_ERROR_WFD_INTERNAL;
2830 if (MM_ERROR_NONE != __mm_wfd_sink_destroy_videobin(wfd_sink)) {
2831 wfd_sink_error("failed to destroy videobin\n");
2832 return MM_ERROR_WFD_INTERNAL;
2835 if (MM_ERROR_NONE != __mm_wfd_sink_destroy_audiobin(wfd_sink)) {
2836 wfd_sink_error("failed to destroy audiobin\n");
2837 return MM_ERROR_WFD_INTERNAL;
2840 gst_object_unref(GST_OBJECT(mainbin[WFD_SINK_M_PIPE].gst));
2842 MMWFDSINK_FREEIF(audiobin);
2843 MMWFDSINK_FREEIF(videobin);
2844 MMWFDSINK_FREEIF(mainbin);
2847 MMWFDSINK_FREEIF(wfd_sink->pipeline);
2850 wfd_sink->added_av_pad_num = 0;
2851 wfd_sink->audio_bin_is_linked = FALSE;
2852 wfd_sink->video_bin_is_linked = FALSE;
2853 wfd_sink->need_to_reset_basetime = FALSE;
2855 wfd_sink_debug_fleave();
2857 return MM_ERROR_NONE;
2861 __mm_wfd_sink_dump_pipeline_state(mm_wfd_sink_t *wfd_sink)
2863 GstIterator *iter = NULL;
2864 gboolean done = FALSE;
2866 GstElement *item = NULL;
2867 GstElementFactory *factory = NULL;
2869 GstState state = GST_STATE_VOID_PENDING;
2870 GstState pending = GST_STATE_VOID_PENDING;
2871 GstClockTime time = 200 * GST_MSECOND;
2873 wfd_sink_debug_fenter();
2875 wfd_sink_return_if_fail(wfd_sink &&
2876 wfd_sink->pipeline &&
2877 wfd_sink->pipeline->mainbin &&
2878 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst);
2880 iter = gst_bin_iterate_recurse(GST_BIN(wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst));
2884 switch (gst_iterator_next(iter, (gpointer)&item)) {
2885 case GST_ITERATOR_OK:
2886 gst_element_get_state(GST_ELEMENT(item), &state, &pending, time);
2888 factory = gst_element_get_factory(item) ;
2890 wfd_sink_error("%s:%s : From:%s To:%s refcount : %d\n",
2891 GST_STR_NULL(GST_OBJECT_NAME(factory)),
2892 GST_STR_NULL(GST_ELEMENT_NAME(item)),
2893 gst_element_state_get_name(state),
2894 gst_element_state_get_name(pending),
2895 GST_OBJECT_REFCOUNT_VALUE(item));
2897 gst_object_unref(item);
2899 case GST_ITERATOR_RESYNC:
2900 gst_iterator_resync(iter);
2902 case GST_ITERATOR_ERROR:
2905 case GST_ITERATOR_DONE:
2915 item = GST_ELEMENT_CAST(wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst);
2917 gst_element_get_state(GST_ELEMENT(item), &state, &pending, time);
2919 factory = gst_element_get_factory(item) ;
2921 wfd_sink_error("%s:%s : From:%s To:%s refcount : %d\n",
2922 GST_OBJECT_NAME(factory),
2923 GST_ELEMENT_NAME(item),
2924 gst_element_state_get_name(state),
2925 gst_element_state_get_name(pending),
2926 GST_OBJECT_REFCOUNT_VALUE(item));
2930 gst_iterator_free(iter);
2932 wfd_sink_debug_fleave();
2938 _mm_wfds_sink_get_state_name(MMWFDSinkStateType state)
2941 case MM_WFD_SINK_STATE_NONE:
2943 case MM_WFD_SINK_STATE_NULL:
2945 case MM_WFD_SINK_STATE_PREPARED:
2947 case MM_WFD_SINK_STATE_CONNECTED:
2949 case MM_WFD_SINK_STATE_PLAYING:
2951 case MM_WFD_SINK_STATE_PAUSED:
2953 case MM_WFD_SINK_STATE_DISCONNECTED:
2954 return "DISCONNECTED";
2960 static void __mm_wfd_sink_prepare_video_resolution(gint resolution, guint *CEA_resolution,
2961 guint *VESA_resolution, guint *HH_resolution)
2963 if (resolution == MM_WFD_SINK_RESOLUTION_UNKNOWN) return;
2965 *CEA_resolution = 0;
2966 *VESA_resolution = 0;
2969 if (resolution & MM_WFD_SINK_RESOLUTION_1920x1080_P30)
2970 *CEA_resolution |= WFD_CEA_1920x1080P30;
2972 if (resolution & MM_WFD_SINK_RESOLUTION_1280x720_P30)
2973 *CEA_resolution |= WFD_CEA_1280x720P30;
2975 if (resolution & MM_WFD_SINK_RESOLUTION_960x540_P30)
2976 *HH_resolution |= WFD_HH_960x540P30;
2978 if (resolution & MM_WFD_SINK_RESOLUTION_864x480_P30)
2979 *HH_resolution |= WFD_HH_864x480P30;
2981 if (resolution & MM_WFD_SINK_RESOLUTION_720x480_P60)
2982 *CEA_resolution |= WFD_CEA_720x480P60;
2984 if (resolution & MM_WFD_SINK_RESOLUTION_640x480_P60)
2985 *CEA_resolution |= WFD_CEA_640x480P60;
2987 if (resolution & MM_WFD_SINK_RESOLUTION_640x360_P30)
2988 *HH_resolution |= WFD_HH_640x360P30;
2991 int _mm_wfd_sink_set_resolution(mm_wfd_sink_t *wfd_sink, MMWFDSinkResolution resolution)
2993 MMWFDSinkStateType cur_state = MM_WFD_SINK_STATE_NONE;
2995 wfd_sink_debug_fenter();
2997 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
2999 MMWFDSINK_PRINT_STATE(wfd_sink);
3000 cur_state = MMWFDSINK_CURRENT_STATE(wfd_sink);
3001 if (cur_state != MM_WFD_SINK_STATE_NULL) {
3002 wfd_sink_error("This function must be called when MM_WFD_SINK_STATE_NULL");
3003 return MM_ERROR_WFD_INVALID_STATE;
3006 wfd_sink->supportive_resolution = resolution;
3008 wfd_sink_debug_fleave();
3010 return MM_ERROR_NONE;