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 const gchar *__mm_wfds_sink_get_state_name(MMWFDSinkStateType state);
48 static void __mm_wfd_sink_prepare_video_resolution(gint resolution, guint *CEA_resolution,
49 guint *VESA_resolution, guint *HH_resolution);
51 int _mm_wfd_sink_create(mm_wfd_sink_t **wfd_sink)
53 int result = MM_ERROR_NONE;
55 wfd_sink_debug_fenter();
57 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
59 mm_wfd_sink_t *new_wfd_sink = NULL;
62 new_wfd_sink = g_malloc0(sizeof(mm_wfd_sink_t));
64 wfd_sink_error("failed to allocate memory for wi-fi display sink\n");
65 return MM_ERROR_WFD_NO_FREE_SPACE;
68 /* Initialize gstreamer related */
69 new_wfd_sink->attrs = 0;
71 new_wfd_sink->pipeline = NULL;
72 new_wfd_sink->added_av_pad_num = 0;
73 new_wfd_sink->audio_bin_is_linked = FALSE;
74 new_wfd_sink->video_bin_is_linked = FALSE;
75 new_wfd_sink->prev_audio_dec_src_pad = NULL;
76 new_wfd_sink->next_audio_dec_sink_pad = NULL;
78 /* Initialize timestamp compensation related */
79 new_wfd_sink->need_to_reset_basetime = FALSE;
80 new_wfd_sink->clock = NULL;
81 new_wfd_sink->video_buffer_count = 0LL;
82 new_wfd_sink->video_average_gap = 0LL;
83 new_wfd_sink->video_accumulated_gap = 0LL;
84 new_wfd_sink->audio_buffer_count = 0LL;
85 new_wfd_sink->audio_average_gap = 0LL;
86 new_wfd_sink->audio_accumulated_gap = 0LL;
87 new_wfd_sink->last_buffer_timestamp = GST_CLOCK_TIME_NONE;
89 /* Initialize all states */
90 MMWFDSINK_CURRENT_STATE(new_wfd_sink) = MM_WFD_SINK_STATE_NONE;
91 MMWFDSINK_PREVIOUS_STATE(new_wfd_sink) = MM_WFD_SINK_STATE_NONE;
92 MMWFDSINK_PENDING_STATE(new_wfd_sink) = MM_WFD_SINK_STATE_NONE;
94 /* initialize audio/video information */
95 new_wfd_sink->stream_info.audio_stream_info.codec = WFD_SINK_AUDIO_CODEC_NONE;
96 new_wfd_sink->stream_info.audio_stream_info.channels = 0;
97 new_wfd_sink->stream_info.audio_stream_info.sample_rate = 0;
98 new_wfd_sink->stream_info.audio_stream_info.bitwidth = 0;
99 new_wfd_sink->stream_info.video_stream_info.codec = WFD_SINK_VIDEO_CODEC_NONE;
100 new_wfd_sink->stream_info.video_stream_info.width = 0;
101 new_wfd_sink->stream_info.video_stream_info.height = 0;
102 new_wfd_sink->stream_info.video_stream_info.frame_rate = 0;
104 /* Initialize command */
105 new_wfd_sink->cmd = MM_WFD_SINK_COMMAND_CREATE;
106 new_wfd_sink->waiting_cmd = FALSE;
108 /* Initialize resource related */
109 new_wfd_sink->resource_list = NULL;
111 /* Initialize manager related */
112 new_wfd_sink->manager_thread = NULL;
113 new_wfd_sink->manager_thread_cmd = WFD_SINK_MANAGER_CMD_NONE;
115 /* Initialize video resolution */
116 new_wfd_sink->supportive_resolution = MM_WFD_SINK_RESOLUTION_UNKNOWN;
118 /* construct attributes */
119 new_wfd_sink->attrs = _mmwfd_construct_attribute((MMHandleType)new_wfd_sink);
120 if (!new_wfd_sink->attrs) {
121 MMWFDSINK_FREEIF(new_wfd_sink);
122 wfd_sink_error("failed to set attribute\n");
123 return MM_ERROR_WFD_INTERNAL;
126 /* load ini for initialize */
127 result = mm_wfd_sink_ini_load(&new_wfd_sink->ini);
128 if (result != MM_ERROR_NONE) {
129 wfd_sink_error("failed to load ini file\n");
130 goto fail_to_load_ini;
132 new_wfd_sink->need_to_reset_basetime = new_wfd_sink->ini.enable_reset_basetime;
134 /* initialize manager */
135 result = _mm_wfd_sink_init_manager(new_wfd_sink);
136 if (result < MM_ERROR_NONE) {
137 wfd_sink_error("failed to init manager : %d\n", result);
141 /* initialize gstreamer */
142 result = __mm_wfd_sink_init_gstreamer(new_wfd_sink);
143 if (result < MM_ERROR_NONE) {
144 wfd_sink_error("failed to init gstreamer : %d\n", result);
149 __mm_wfd_sink_set_state(new_wfd_sink, MM_WFD_SINK_STATE_NULL);
151 /* now take handle */
152 *wfd_sink = new_wfd_sink;
154 wfd_sink_debug_fleave();
160 mm_wfd_sink_ini_unload(&new_wfd_sink->ini);
162 _mmwfd_deconstruct_attribute(new_wfd_sink->attrs);
163 MMWFDSINK_FREEIF(new_wfd_sink);
170 int _mm_wfd_sink_prepare(mm_wfd_sink_t *wfd_sink)
172 int result = MM_ERROR_NONE;
174 wfd_sink_debug_fenter();
176 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
178 /* check current wi-fi display sink state */
179 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_PREPARE);
181 /* construct pipeline */
182 /* create main pipeline */
183 result = __mm_wfd_sink_create_pipeline(wfd_sink);
184 if (result < MM_ERROR_NONE) {
185 wfd_sink_error("failed to create pipeline : %d\n", result);
189 /* create videobin */
190 result = __mm_wfd_sink_create_videobin(wfd_sink);
191 if (result < MM_ERROR_NONE) {
192 wfd_sink_error("failed to create videobin : %d\n", result);
196 /* create audiobin */
197 result = __mm_wfd_sink_create_audiobin(wfd_sink);
198 if (result < MM_ERROR_NONE) {
199 wfd_sink_error("fail to create audiobin : %d\n", result);
203 /* set pipeline READY state */
204 result = __mm_wfd_sink_set_pipeline_state(wfd_sink, GST_STATE_READY, TRUE);
205 if (result < MM_ERROR_NONE) {
206 wfd_sink_error("failed to set state : %d\n", result);
211 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_PREPARED);
213 wfd_sink_debug_fleave();
219 /* need to destroy pipeline already created */
220 __mm_wfd_sink_destroy_pipeline(wfd_sink);
224 int _mm_wfd_sink_connect(mm_wfd_sink_t *wfd_sink, const char *uri)
226 int result = MM_ERROR_NONE;
228 wfd_sink_debug_fenter();
230 wfd_sink_return_val_if_fail(uri && strlen(uri) > strlen("rtsp://"),
231 MM_ERROR_WFD_INVALID_ARGUMENT);
232 wfd_sink_return_val_if_fail(wfd_sink &&
233 wfd_sink->pipeline &&
234 wfd_sink->pipeline->mainbin &&
235 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst &&
236 wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst &&
237 wfd_sink->pipeline->mainbin[WFD_SINK_M_DEPAY].gst &&
238 wfd_sink->pipeline->mainbin[WFD_SINK_M_DEMUX].gst,
239 MM_ERROR_WFD_NOT_INITIALIZED);
241 /* check current wi-fi display sink state */
242 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_CONNECT);
244 wfd_sink_debug("try to connect to %s.....\n", GST_STR_NULL(uri));
246 /* set uri to wfdrtspsrc */
247 g_object_set(G_OBJECT(wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst), "location", uri, NULL);
249 /* set pipeline PAUSED state */
250 result = __mm_wfd_sink_set_pipeline_state(wfd_sink, GST_STATE_PAUSED, TRUE);
251 if (result < MM_ERROR_NONE) {
252 wfd_sink_error("failed to set state : %d\n", result);
256 wfd_sink_debug_fleave();
261 int _mm_wfd_sink_start(mm_wfd_sink_t *wfd_sink)
263 int result = MM_ERROR_NONE;
265 wfd_sink_debug_fenter();
267 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
269 /* check current wi-fi display sink state */
270 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_START);
272 WFD_SINK_MANAGER_LOCK(wfd_sink) ;
273 wfd_sink_debug("check pipeline is ready to start");
274 WFD_SINK_MANAGER_UNLOCK(wfd_sink);
276 result = __mm_wfd_sink_set_pipeline_state(wfd_sink, GST_STATE_PLAYING, TRUE);
277 if (result < MM_ERROR_NONE) {
278 wfd_sink_error("failed to set state : %d\n", result);
282 wfd_sink_debug_fleave();
287 int _mm_wfd_sink_pause(mm_wfd_sink_t *wfd_sink)
289 int result = MM_ERROR_NONE;
291 wfd_sink_debug_fenter();
293 wfd_sink_return_val_if_fail(wfd_sink &&
294 wfd_sink->pipeline &&
295 wfd_sink->pipeline->mainbin &&
296 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst &&
297 wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst,
298 MM_ERROR_WFD_NOT_INITIALIZED);
300 /* check current wi-fi display sink state */
301 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_PAUSE);
303 g_signal_emit_by_name(wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst, "pause", NULL);
305 wfd_sink_debug_fleave();
310 int _mm_wfd_sink_resume(mm_wfd_sink_t *wfd_sink)
312 int result = MM_ERROR_NONE;
314 wfd_sink_debug_fenter();
316 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
318 /* check current wi-fi display sink state */
319 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_RESUME);
321 g_signal_emit_by_name(wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst, "resume", NULL);
323 wfd_sink_debug_fleave();
328 int _mm_wfd_sink_disconnect(mm_wfd_sink_t *wfd_sink)
330 int result = MM_ERROR_NONE;
332 wfd_sink_debug_fenter();
334 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
336 /* check current wi-fi display sink state */
337 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_DISCONNECT);
339 WFD_SINK_MANAGER_LOCK(wfd_sink) ;
340 WFD_SINK_MANAGER_SIGNAL_CMD(wfd_sink, WFD_SINK_MANAGER_CMD_EXIT);
341 WFD_SINK_MANAGER_UNLOCK(wfd_sink);
343 g_signal_emit_by_name(wfd_sink->pipeline->mainbin[WFD_SINK_M_SRC].gst, "close", NULL);
346 wfd_sink_debug_fleave();
351 int _mm_wfd_sink_unprepare(mm_wfd_sink_t *wfd_sink)
353 int result = MM_ERROR_NONE;
355 wfd_sink_debug_fenter();
357 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
359 /* check current wi-fi display sink state */
360 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_UNPREPARE);
362 WFD_SINK_MANAGER_LOCK(wfd_sink) ;
363 WFD_SINK_MANAGER_SIGNAL_CMD(wfd_sink, WFD_SINK_MANAGER_CMD_EXIT);
364 WFD_SINK_MANAGER_UNLOCK(wfd_sink);
366 /* release pipeline */
367 result = __mm_wfd_sink_destroy_pipeline(wfd_sink);
368 if (result != MM_ERROR_NONE) {
369 wfd_sink_error("failed to destory pipeline\n");
370 return MM_ERROR_WFD_INTERNAL;
372 wfd_sink_debug("success to destory pipeline\n");
376 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_NULL);
378 wfd_sink_debug_fleave();
383 int _mm_wfd_sink_destroy(mm_wfd_sink_t *wfd_sink)
385 int result = MM_ERROR_NONE;
387 wfd_sink_debug_fenter();
389 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
391 /* check current wi-fi display sink state */
392 MMWFDSINK_CHECK_STATE(wfd_sink, MM_WFD_SINK_COMMAND_DESTROY);
395 mm_wfd_sink_ini_unload(&wfd_sink->ini);
397 /* release attributes */
398 _mmwfd_deconstruct_attribute(wfd_sink->attrs);
400 if (MM_ERROR_NONE != _mm_wfd_sink_release_manager(wfd_sink)) {
401 wfd_sink_error("failed to release manager\n");
402 return MM_ERROR_WFD_INTERNAL;
407 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_NONE);
409 wfd_sink_debug_fleave();
414 int _mm_wfd_set_message_callback(mm_wfd_sink_t *wfd_sink, MMWFDMessageCallback callback, void *user_data)
416 int result = MM_ERROR_NONE;
418 wfd_sink_debug_fenter();
420 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
422 wfd_sink->msg_cb = callback;
423 wfd_sink->msg_user_data = user_data;
425 wfd_sink_debug_fleave();
430 static int __mm_wfd_sink_init_gstreamer(mm_wfd_sink_t *wfd_sink)
432 int result = MM_ERROR_NONE;
435 static const int max_argc = 50;
439 wfd_sink_debug_fenter();
442 argc = calloc(1, sizeof(gint));
443 argv = calloc(max_argc, sizeof(gchar *));
444 if (!argc || !argv) {
445 wfd_sink_error("failed to allocate memory for wfdsink\n");
447 MMWFDSINK_FREEIF(argv);
448 MMWFDSINK_FREEIF(argc);
450 return MM_ERROR_WFD_NO_FREE_SPACE;
453 /* we would not do fork for scanning plugins */
454 argv[*argc] = g_strdup("--gst-disable-registry-fork");
457 /* check disable registry scan */
458 argv[*argc] = g_strdup("--gst-disable-registry-update");
461 /* check disable segtrap */
462 argv[*argc] = g_strdup("--gst-disable-segtrap");
466 for (i = 0; i < 5; i++) {
467 if (strlen(wfd_sink->ini.gst_param[i]) > 2) {
468 wfd_sink_debug("set %s\n", wfd_sink->ini.gst_param[i]);
469 argv[*argc] = g_strdup(wfd_sink->ini.gst_param[i]);
474 wfd_sink_debug("initializing gstreamer with following parameter\n");
475 wfd_sink_debug("argc : %d\n", *argc);
477 for (i = 0; i < *argc; i++) {
478 wfd_sink_debug("argv[%d] : %s\n", i, argv[i]);
481 /* initializing gstreamer */
482 if (!gst_init_check(argc, &argv, &err)) {
483 wfd_sink_error("failed to initialize gstreamer: %s\n",
484 err ? err->message : "unknown error occurred");
488 result = MM_ERROR_WFD_INTERNAL;
492 for (i = 0; i < *argc; i++) {
493 MMWFDSINK_FREEIF(argv[i]);
495 MMWFDSINK_FREEIF(argv);
496 MMWFDSINK_FREEIF(argc);
498 wfd_sink_debug_fleave();
504 _mm_wfd_sink_correct_pipeline_latency(mm_wfd_sink_t *wfd_sink)
507 GstClockTime min_latency;
509 qlatency = gst_query_new_latency();
510 gst_element_query(wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst, qlatency);
511 gst_query_parse_latency(qlatency, NULL, &min_latency, NULL);
513 debug_msg("Correct manually pipeline latency: current=%"GST_TIME_FORMAT, GST_TIME_ARGS(min_latency));
514 g_object_set(wfd_sink->pipeline->videobin[WFD_SINK_V_SINK].gst, "ts-offset", -(gint64)(min_latency * 9 / 10), NULL);
515 g_object_set(wfd_sink->pipeline->audiobin[WFD_SINK_A_SINK].gst, "ts-offset", -(gint64)(min_latency * 9 / 10), NULL);
516 gst_query_unref(qlatency);
519 static GstBusSyncReply
520 _mm_wfd_bus_sync_callback(GstBus *bus, GstMessage *message, gpointer data)
522 GstBusSyncReply ret = GST_BUS_PASS;
524 wfd_sink_return_val_if_fail(message &&
525 GST_IS_MESSAGE(message) &&
526 GST_MESSAGE_SRC(message),
529 switch (GST_MESSAGE_TYPE(message)) {
530 case GST_MESSAGE_TAG:
532 case GST_MESSAGE_DURATION:
534 case GST_MESSAGE_STATE_CHANGED: {
535 /* we only handle state change messages from pipeline */
536 if (!GST_IS_PIPELINE(GST_MESSAGE_SRC(message)))
540 case GST_MESSAGE_ASYNC_DONE: {
541 if (!GST_IS_PIPELINE(GST_MESSAGE_SRC(message)))
553 _mm_wfd_sink_msg_callback(GstBus *bus, GstMessage *msg, gpointer data)
555 mm_wfd_sink_t *wfd_sink = (mm_wfd_sink_t *) data;
556 const GstStructure *message_structure = gst_message_get_structure(msg);
559 wfd_sink_return_val_if_fail(wfd_sink, FALSE);
560 wfd_sink_return_val_if_fail(msg && GST_IS_MESSAGE(msg), FALSE);
562 wfd_sink_debug("got %s from %s \n",
563 GST_STR_NULL(GST_MESSAGE_TYPE_NAME(msg)),
564 GST_STR_NULL(GST_OBJECT_NAME(GST_MESSAGE_SRC(msg))));
566 switch (GST_MESSAGE_TYPE(msg)) {
567 case GST_MESSAGE_ERROR: {
568 GError *error = NULL;
572 gst_message_parse_error(msg, &error, &debug);
574 wfd_sink_error("error : %s\n", error->message);
575 wfd_sink_error("debug : %s\n", debug);
577 MMWFDSINK_FREEIF(debug);
582 case GST_MESSAGE_WARNING: {
584 GError *error = NULL;
586 gst_message_parse_warning(msg, &error, &debug);
588 wfd_sink_error("warning : %s\n", error->message);
589 wfd_sink_error("debug : %s\n", debug);
591 MMWFDSINK_FREEIF(debug);
596 case GST_MESSAGE_STATE_CHANGED: {
597 const GValue *voldstate, *vnewstate, *vpending;
598 GstState oldstate, newstate, pending;
599 const GstStructure *structure;
601 /* we only handle messages from pipeline */
602 if (msg->src != (GstObject *)wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst)
605 /* get state info from msg */
606 structure = gst_message_get_structure(msg);
607 if (structure == NULL)
610 voldstate = gst_structure_get_value(structure, "old-state");
611 vnewstate = gst_structure_get_value(structure, "new-state");
612 vpending = gst_structure_get_value(structure, "pending-state");
614 oldstate = (GstState)voldstate->data[0].v_int;
615 newstate = (GstState)vnewstate->data[0].v_int;
616 pending = (GstState)vpending->data[0].v_int;
618 wfd_sink_debug("state changed [%s] : %s--->%s final : %s\n",
619 GST_OBJECT_NAME(GST_MESSAGE_SRC(msg)),
620 gst_element_state_get_name((GstState)oldstate),
621 gst_element_state_get_name((GstState)newstate),
622 gst_element_state_get_name((GstState)pending));
624 if (oldstate == newstate) {
625 wfd_sink_error("pipeline reports state transition to old state\n");
630 case GST_STATE_VOID_PENDING:
632 case GST_STATE_READY:
633 case GST_STATE_PAUSED:
634 case GST_STATE_PLAYING:
641 case GST_MESSAGE_CLOCK_LOST: {
642 GstClock *clock = NULL;
643 gst_message_parse_clock_lost(msg, &clock);
644 wfd_sink_debug("The current clock[%s] as selected by the pipeline became unusable.",
645 (clock ? GST_OBJECT_NAME(clock) : "NULL"));
649 case GST_MESSAGE_NEW_CLOCK: {
650 GstClock *clock = NULL;
651 gst_message_parse_new_clock(msg, &clock);
655 if (wfd_sink->clock) {
656 if (wfd_sink->clock != clock)
657 wfd_sink_debug("clock is changed! [%s] -->[%s]\n",
658 GST_STR_NULL(GST_OBJECT_NAME(wfd_sink->clock)),
659 GST_STR_NULL(GST_OBJECT_NAME(clock)));
661 wfd_sink_debug("same clock is selected again! [%s] \n",
662 GST_STR_NULL(GST_OBJECT_NAME(clock)));
664 wfd_sink_debug("new clock [%s] was selected in the pipeline\n",
665 (GST_STR_NULL(GST_OBJECT_NAME(clock))));
668 wfd_sink->clock = clock;
672 case GST_MESSAGE_APPLICATION: {
673 const gchar *message_structure_name;
675 message_structure_name = gst_structure_get_name(message_structure);
676 if (!message_structure_name)
679 wfd_sink_debug("message name : %s", GST_STR_NULL(message_structure_name));
683 case GST_MESSAGE_ELEMENT: {
684 const gchar *structure_name = NULL;
685 const GstStructure *message_structure = NULL;
687 message_structure = gst_message_get_structure(msg);
688 structure_name = gst_structure_get_name(message_structure);
689 if (structure_name) {
690 wfd_sink_debug("got element specific message[%s]\n", GST_STR_NULL(structure_name));
691 if (g_strrstr(structure_name, "GstUDPSrcTimeout")) {
692 wfd_sink_error("Got %s, post error message\n", GST_STR_NULL(structure_name));
693 MMWFDSINK_POST_MESSAGE(wfd_sink,
694 MM_ERROR_WFD_INTERNAL,
695 MMWFDSINK_CURRENT_STATE(wfd_sink));
701 case GST_MESSAGE_PROGRESS: {
702 GstProgressType type = GST_PROGRESS_TYPE_ERROR;
703 gchar *category = NULL, *text = NULL;
705 gst_message_parse_progress(msg, &type, &category, &text);
706 wfd_sink_debug("%s : %s \n", GST_STR_NULL(category), GST_STR_NULL(text));
709 case GST_PROGRESS_TYPE_START:
711 case GST_PROGRESS_TYPE_COMPLETE:
712 if (category && !strcmp(category, "open"))
713 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_CONNECTED);
714 else if (category && !strcmp(category, "play")) {
715 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_PLAYING);
716 /*_mm_wfd_sink_correct_pipeline_latency (wfd_sink); */
717 } else if (category && !strcmp(category, "pause"))
718 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_PAUSED);
719 else if (category && !strcmp(category, "close"))
720 __mm_wfd_sink_set_state(wfd_sink, MM_WFD_SINK_STATE_DISCONNECTED);
724 case GST_PROGRESS_TYPE_CANCELED:
726 case GST_PROGRESS_TYPE_ERROR:
727 if (category && !strcmp(category, "open")) {
728 wfd_sink_error("got error : %s\n", GST_STR_NULL(text));
729 /*_mm_wfd_sink_disconnect (wfd_sink); */
730 MMWFDSINK_POST_MESSAGE(wfd_sink,
731 MM_ERROR_WFD_INTERNAL,
732 MMWFDSINK_CURRENT_STATE(wfd_sink));
733 } else if (category && !strcmp(category, "play")) {
734 wfd_sink_error("got error : %s\n", GST_STR_NULL(text));
735 /*_mm_wfd_sink_disconnect (wfd_sink); */
736 MMWFDSINK_POST_MESSAGE(wfd_sink,
737 MM_ERROR_WFD_INTERNAL,
738 MMWFDSINK_CURRENT_STATE(wfd_sink));
739 } else if (category && !strcmp(category, "pause")) {
740 wfd_sink_error("got error : %s\n", GST_STR_NULL(text));
741 /*_mm_wfd_sink_disconnect (wfd_sink); */
742 MMWFDSINK_POST_MESSAGE(wfd_sink,
743 MM_ERROR_WFD_INTERNAL,
744 MMWFDSINK_CURRENT_STATE(wfd_sink));
745 } else if (category && !strcmp(category, "close")) {
746 wfd_sink_error("got error : %s\n", GST_STR_NULL(text));
747 /*_mm_wfd_sink_disconnect (wfd_sink); */
748 MMWFDSINK_POST_MESSAGE(wfd_sink,
749 MM_ERROR_WFD_INTERNAL,
750 MMWFDSINK_CURRENT_STATE(wfd_sink));
752 wfd_sink_error("got error : %s\n", GST_STR_NULL(text));
756 wfd_sink_error("progress message has no type\n");
760 MMWFDSINK_FREEIF(category);
761 MMWFDSINK_FREEIF(text);
764 case GST_MESSAGE_ASYNC_START:
765 wfd_sink_debug("GST_MESSAGE_ASYNC_START : %s\n", gst_element_get_name(GST_MESSAGE_SRC(msg)));
767 case GST_MESSAGE_ASYNC_DONE:
768 wfd_sink_debug("GST_MESSAGE_ASYNC_DONE : %s\n", gst_element_get_name(GST_MESSAGE_SRC(msg)));
770 case GST_MESSAGE_UNKNOWN:
771 case GST_MESSAGE_INFO:
772 case GST_MESSAGE_TAG:
773 case GST_MESSAGE_BUFFERING:
774 case GST_MESSAGE_EOS:
775 case GST_MESSAGE_STATE_DIRTY:
776 case GST_MESSAGE_STEP_DONE:
777 case GST_MESSAGE_CLOCK_PROVIDE:
778 case GST_MESSAGE_STRUCTURE_CHANGE:
779 case GST_MESSAGE_STREAM_STATUS:
780 case GST_MESSAGE_SEGMENT_START:
781 case GST_MESSAGE_SEGMENT_DONE:
782 case GST_MESSAGE_DURATION:
783 case GST_MESSAGE_LATENCY:
784 case GST_MESSAGE_REQUEST_STATE:
785 case GST_MESSAGE_STEP_START:
786 case GST_MESSAGE_QOS:
787 case GST_MESSAGE_ANY:
790 wfd_sink_debug("unhandled message\n");
798 __mm_wfd_sink_gst_element_add_bucket_to_bin(GstBin *bin, GList *element_bucket, gboolean need_prepare)
800 GList *bucket = element_bucket;
801 MMWFDSinkGstElement *element = NULL;
802 int successful_add_count = 0;
804 wfd_sink_debug_fenter();
806 wfd_sink_return_val_if_fail(element_bucket, 0);
807 wfd_sink_return_val_if_fail(bin, 0);
809 for (; bucket; bucket = bucket->next) {
810 element = (MMWFDSinkGstElement *)bucket->data;
812 if (element && element->gst) {
814 gst_element_set_state(GST_ELEMENT(element->gst), GST_STATE_READY);
816 if (!gst_bin_add(bin, GST_ELEMENT(element->gst))) {
817 wfd_sink_error("failed to add element [%s] to bin [%s]\n",
818 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT(element->gst))),
819 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT_CAST(bin))));
823 wfd_sink_debug("add element [%s] to bin [%s]\n",
824 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT(element->gst))),
825 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT_CAST(bin))));
827 successful_add_count++;
831 wfd_sink_debug_fleave();
833 return successful_add_count;
837 __mm_wfd_sink_gst_element_link_bucket(GList *element_bucket)
839 GList *bucket = element_bucket;
840 MMWFDSinkGstElement *element = NULL;
841 MMWFDSinkGstElement *prv_element = NULL;
842 gint successful_link_count = 0;
844 wfd_sink_debug_fenter();
846 wfd_sink_return_val_if_fail(element_bucket, -1);
848 prv_element = (MMWFDSinkGstElement *)bucket->data;
849 bucket = bucket->next;
851 for (; bucket; bucket = bucket->next) {
852 element = (MMWFDSinkGstElement *)bucket->data;
854 if (element && element->gst) {
855 if (gst_element_link(GST_ELEMENT(prv_element->gst), GST_ELEMENT(element->gst))) {
856 wfd_sink_debug("linking [%s] to [%s] success\n",
857 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT(prv_element->gst))),
858 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT(element->gst))));
859 successful_link_count++;
861 wfd_sink_error("linking [%s] to [%s] failed\n",
862 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT(prv_element->gst))),
863 GST_STR_NULL(GST_ELEMENT_NAME(GST_ELEMENT(element->gst))));
868 prv_element = element;
871 wfd_sink_debug_fleave();
873 return successful_link_count;
877 __mm_wfd_sink_check_state(mm_wfd_sink_t *wfd_sink, MMWFDSinkCommandType cmd)
879 MMWFDSinkStateType cur_state = MM_WFD_SINK_STATE_NONE;
881 wfd_sink_debug_fenter();
883 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
885 MMWFDSINK_PRINT_STATE(wfd_sink);
887 cur_state = MMWFDSINK_CURRENT_STATE(wfd_sink);
890 case MM_WFD_SINK_COMMAND_CREATE: {
891 if (cur_state != MM_WFD_SINK_STATE_NONE)
894 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_NULL;
898 case MM_WFD_SINK_COMMAND_PREPARE: {
899 if (cur_state == MM_WFD_SINK_STATE_PREPARED)
901 else if (cur_state != MM_WFD_SINK_STATE_NULL)
904 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_PREPARED;
908 case MM_WFD_SINK_COMMAND_CONNECT: {
909 if (cur_state == MM_WFD_SINK_STATE_CONNECTED)
911 else if (cur_state != MM_WFD_SINK_STATE_PREPARED)
914 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_CONNECTED;
918 case MM_WFD_SINK_COMMAND_START: {
919 if (cur_state == MM_WFD_SINK_STATE_PLAYING)
921 else if (cur_state != MM_WFD_SINK_STATE_CONNECTED)
924 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_PLAYING;
928 case MM_WFD_SINK_COMMAND_PAUSE: {
929 if (cur_state == MM_WFD_SINK_STATE_PAUSED)
931 else if (cur_state != MM_WFD_SINK_STATE_PLAYING)
934 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_PAUSED;
938 case MM_WFD_SINK_COMMAND_RESUME: {
939 if (cur_state == MM_WFD_SINK_STATE_PLAYING)
941 else if (cur_state != MM_WFD_SINK_STATE_PAUSED)
944 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_PLAYING;
948 case MM_WFD_SINK_COMMAND_DISCONNECT: {
949 if (cur_state == MM_WFD_SINK_STATE_NONE ||
950 cur_state == MM_WFD_SINK_STATE_NULL ||
951 cur_state == MM_WFD_SINK_STATE_PREPARED ||
952 cur_state == MM_WFD_SINK_STATE_DISCONNECTED)
954 else if (cur_state != MM_WFD_SINK_STATE_PLAYING &&
955 cur_state != MM_WFD_SINK_STATE_CONNECTED &&
956 cur_state != MM_WFD_SINK_STATE_PAUSED)
959 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_DISCONNECTED;
963 case MM_WFD_SINK_COMMAND_UNPREPARE: {
964 if (cur_state == MM_WFD_SINK_STATE_NONE ||
965 cur_state == MM_WFD_SINK_STATE_NULL)
968 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_NULL;
972 case MM_WFD_SINK_COMMAND_DESTROY: {
973 if (cur_state == MM_WFD_SINK_STATE_NONE)
976 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_NONE;
986 wfd_sink_debug_fleave();
988 return MM_ERROR_NONE;
991 wfd_sink_debug("already %s state, nothing to do.\n", MMWFDSINK_STATE_GET_NAME(cur_state));
992 return MM_ERROR_WFD_NO_OP;
996 wfd_sink_error("current state is invalid.\n", MMWFDSINK_STATE_GET_NAME(cur_state));
997 return MM_ERROR_WFD_INVALID_STATE;
1000 static int __mm_wfd_sink_set_state(mm_wfd_sink_t *wfd_sink, MMWFDSinkStateType state)
1002 wfd_sink_debug_fenter();
1004 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
1006 if (MMWFDSINK_CURRENT_STATE(wfd_sink) == state) {
1007 wfd_sink_error("already state(%s)\n", MMWFDSINK_STATE_GET_NAME(state));
1008 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_NONE;
1009 return MM_ERROR_NONE;
1012 /* update wi-fi display state */
1013 MMWFDSINK_PREVIOUS_STATE(wfd_sink) = MMWFDSINK_CURRENT_STATE(wfd_sink);
1014 MMWFDSINK_CURRENT_STATE(wfd_sink) = state;
1016 if (MMWFDSINK_CURRENT_STATE(wfd_sink) == MMWFDSINK_PENDING_STATE(wfd_sink))
1017 MMWFDSINK_PENDING_STATE(wfd_sink) = MM_WFD_SINK_STATE_NONE;
1019 /* poset state message to application */
1020 MMWFDSINK_POST_MESSAGE(wfd_sink,
1022 MMWFDSINK_CURRENT_STATE(wfd_sink));
1025 MMWFDSINK_PRINT_STATE(wfd_sink);
1027 wfd_sink_debug_fleave();
1029 return MM_ERROR_NONE;
1033 __mm_wfd_sink_set_pipeline_state(mm_wfd_sink_t *wfd_sink, GstState state, gboolean async)
1035 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
1036 GstState cur_state = GST_STATE_VOID_PENDING;
1037 GstState pending_state = GST_STATE_VOID_PENDING;
1039 wfd_sink_debug_fenter();
1041 wfd_sink_return_val_if_fail(wfd_sink &&
1042 wfd_sink->pipeline &&
1043 wfd_sink->pipeline->mainbin &&
1044 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst,
1045 MM_ERROR_WFD_NOT_INITIALIZED);
1047 wfd_sink_return_val_if_fail(state > GST_STATE_VOID_PENDING,
1048 MM_ERROR_WFD_INVALID_ARGUMENT);
1050 wfd_sink_debug("try to set %s state \n", gst_element_state_get_name(state));
1052 result = gst_element_set_state(wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst, state);
1053 if (result == GST_STATE_CHANGE_FAILURE) {
1054 wfd_sink_error("fail to set %s state....\n", gst_element_state_get_name(state));
1055 return MM_ERROR_WFD_INTERNAL;
1059 wfd_sink_debug("wait for changing state is completed \n");
1061 result = gst_element_get_state(wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst,
1062 &cur_state, &pending_state, wfd_sink->ini.state_change_timeout * GST_SECOND);
1063 if (result == GST_STATE_CHANGE_FAILURE) {
1064 wfd_sink_error("fail to get state within %d seconds....\n", wfd_sink->ini.state_change_timeout);
1066 __mm_wfd_sink_dump_pipeline_state(wfd_sink);
1068 return MM_ERROR_WFD_INTERNAL;
1069 } else if (result == GST_STATE_CHANGE_NO_PREROLL) {
1070 wfd_sink_debug("successfully changed state but is not able to provide data yet\n");
1073 wfd_sink_debug("cur state is %s, pending state is %s\n",
1074 gst_element_state_get_name(cur_state),
1075 gst_element_state_get_name(pending_state));
1079 wfd_sink_debug_fleave();
1081 return MM_ERROR_NONE;
1085 _mm_wfd_sink_reset_basetime(mm_wfd_sink_t *wfd_sink)
1087 GstClockTime base_time = GST_CLOCK_TIME_NONE;
1090 wfd_sink_debug_fenter();
1092 wfd_sink_return_if_fail(wfd_sink &&
1093 wfd_sink->pipeline &&
1094 wfd_sink->pipeline->mainbin &&
1095 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst);
1096 wfd_sink_return_if_fail(wfd_sink->need_to_reset_basetime);
1099 if (wfd_sink->clock)
1100 base_time = gst_clock_get_time(wfd_sink->clock);
1102 if (GST_CLOCK_TIME_IS_VALID(base_time)) {
1104 wfd_sink_debug("set pipeline base_time as now [%"GST_TIME_FORMAT"]\n", GST_TIME_ARGS(base_time));
1106 for (i = 0; i < WFD_SINK_M_NUM; i++) {
1107 if (wfd_sink->pipeline->mainbin[i].gst)
1108 gst_element_set_base_time(GST_ELEMENT_CAST(wfd_sink->pipeline->mainbin[i].gst), base_time);
1111 if (wfd_sink->pipeline->videobin) {
1112 for (i = 0; i < WFD_SINK_V_NUM; i++) {
1113 if (wfd_sink->pipeline->videobin[i].gst)
1114 gst_element_set_base_time(GST_ELEMENT_CAST(wfd_sink->pipeline->videobin[i].gst), base_time);
1118 if (wfd_sink->pipeline->audiobin) {
1119 for (i = 0; i < WFD_SINK_A_NUM; i++) {
1120 if (wfd_sink->pipeline->audiobin[i].gst)
1121 gst_element_set_base_time(GST_ELEMENT_CAST(wfd_sink->pipeline->audiobin[i].gst), base_time);
1124 wfd_sink->need_to_reset_basetime = FALSE;
1127 wfd_sink_debug_fleave();
1133 __mm_wfd_sink_prepare_videobin(mm_wfd_sink_t *wfd_sink)
1135 GstElement *videobin = NULL;
1137 wfd_sink_debug_fenter();
1139 wfd_sink_return_val_if_fail(wfd_sink &&
1141 MM_ERROR_WFD_NOT_INITIALIZED);
1143 if (wfd_sink->pipeline->videobin == NULL) {
1144 if (MM_ERROR_NONE != __mm_wfd_sink_create_videobin(wfd_sink)) {
1145 wfd_sink_error("failed to create videobin....\n");
1149 wfd_sink_debug("videobin is already created.\n");
1152 videobin = wfd_sink->pipeline->videobin[WFD_SINK_V_BIN].gst;
1154 if (GST_STATE(videobin) <= GST_STATE_NULL) {
1155 if (GST_STATE_CHANGE_FAILURE == gst_element_set_state(videobin, GST_STATE_READY)) {
1156 wfd_sink_error("failed to set state(READY) to %s\n", GST_STR_NULL(GST_ELEMENT_NAME(videobin)));
1161 wfd_sink_debug_fleave();
1163 return MM_ERROR_NONE;
1167 /* need to notify to app */
1168 MMWFDSINK_POST_MESSAGE(wfd_sink,
1169 MM_ERROR_WFD_INTERNAL,
1170 MMWFDSINK_CURRENT_STATE(wfd_sink));
1172 return MM_ERROR_WFD_INTERNAL;
1176 __mm_wfd_sink_prepare_audiobin(mm_wfd_sink_t *wfd_sink)
1178 MMWFDSinkGstElement *audiobin = NULL;
1180 wfd_sink_debug_fenter();
1182 wfd_sink_return_val_if_fail(wfd_sink &&
1184 MM_ERROR_WFD_NOT_INITIALIZED);
1186 if (wfd_sink->pipeline->audiobin == NULL) {
1187 if (MM_ERROR_NONE != __mm_wfd_sink_create_audiobin(wfd_sink)) {
1188 wfd_sink_error("failed to create audiobin....\n");
1193 if (!wfd_sink->audio_bin_is_linked) {
1194 if (MM_ERROR_NONE != __mm_wfd_sink_link_audiobin(wfd_sink)) {
1195 wfd_sink_error("failed to link audio decoder.....\n");
1200 audiobin = wfd_sink->pipeline->audiobin;
1202 if (GST_STATE(audiobin) <= GST_STATE_NULL) {
1203 if (GST_STATE_CHANGE_FAILURE == gst_element_set_state(audiobin[WFD_SINK_A_BIN].gst, GST_STATE_READY)) {
1204 wfd_sink_error("failed to set state(READY) to %s\n",
1205 GST_STR_NULL(GST_ELEMENT_NAME(audiobin)));
1210 wfd_sink_debug_fleave();
1212 return MM_ERROR_NONE;
1216 /* need to notify to app */
1217 MMWFDSINK_POST_MESSAGE(wfd_sink,
1218 MM_ERROR_WFD_INTERNAL,
1219 MMWFDSINK_CURRENT_STATE(wfd_sink));
1221 return MM_ERROR_WFD_INTERNAL;
1224 #define COMPENSATION_CRETERIA_VALUE 1000000 /* 1 msec */
1225 #define COMPENSATION_CHECK_PERIOD (30*GST_SECOND) /* 30 sec */
1227 static GstPadProbeReturn
1228 _mm_wfd_sink_check_running_time(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1230 mm_wfd_sink_t *wfd_sink = (mm_wfd_sink_t *)u_data;
1231 GstClockTime current_time = GST_CLOCK_TIME_NONE;
1232 GstClockTime start_time = GST_CLOCK_TIME_NONE;
1233 GstClockTime running_time = GST_CLOCK_TIME_NONE;
1234 GstClockTime base_time = GST_CLOCK_TIME_NONE;
1235 GstClockTime render_time = GST_CLOCK_TIME_NONE;
1236 GstClockTimeDiff diff = GST_CLOCK_TIME_NONE;
1237 GstBuffer *buffer = NULL;
1238 gint64 ts_offset = 0LL;
1240 wfd_sink_return_val_if_fail(info, FALSE);
1241 wfd_sink_return_val_if_fail(wfd_sink &&
1242 wfd_sink->pipeline &&
1243 wfd_sink->pipeline->mainbin &&
1244 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst,
1245 GST_PAD_PROBE_DROP);
1247 if (!wfd_sink->clock) {
1248 wfd_sink_warning("pipeline did not select clock, yet\n");
1249 return GST_PAD_PROBE_OK;
1252 if (wfd_sink->need_to_reset_basetime)
1253 _mm_wfd_sink_reset_basetime(wfd_sink);
1255 /* calculate current runninig time */
1256 current_time = gst_clock_get_time(wfd_sink->clock);
1257 if (g_strrstr(GST_OBJECT_NAME(pad), "video"))
1258 base_time = gst_element_get_base_time(wfd_sink->pipeline->videobin[WFD_SINK_V_BIN].gst);
1259 else if (g_strrstr(GST_OBJECT_NAME(pad), "audio"))
1260 base_time = gst_element_get_base_time(wfd_sink->pipeline->audiobin[WFD_SINK_A_BIN].gst);
1261 start_time = gst_element_get_start_time(wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst);
1262 if (GST_CLOCK_TIME_IS_VALID(current_time) &&
1263 GST_CLOCK_TIME_IS_VALID(start_time) &&
1264 GST_CLOCK_TIME_IS_VALID(base_time)) {
1265 running_time = current_time - (start_time + base_time);
1267 wfd_sink_debug("current time %"GST_TIME_FORMAT", start time %"GST_TIME_FORMAT
1268 " base time %"GST_TIME_FORMAT"\n", GST_TIME_ARGS(current_time),
1269 GST_TIME_ARGS(start_time), GST_TIME_ARGS(base_time));
1270 return GST_PAD_PROBE_OK;
1273 /* calculate this buffer rendering time */
1274 buffer = gst_pad_probe_info_get_buffer(info);
1275 if (!GST_BUFFER_TIMESTAMP_IS_VALID(buffer)) {
1276 wfd_sink_error("buffer timestamp is invalid.\n");
1277 return GST_PAD_PROBE_OK;
1280 if (g_strrstr(GST_OBJECT_NAME(pad), "audio")) {
1281 if (wfd_sink->pipeline && wfd_sink->pipeline->audiobin && wfd_sink->pipeline->audiobin[WFD_SINK_A_SINK].gst)
1282 g_object_get(G_OBJECT(wfd_sink->pipeline->audiobin[WFD_SINK_A_SINK].gst), "ts-offset", &ts_offset, NULL);
1283 } else if (g_strrstr(GST_OBJECT_NAME(pad), "video")) {
1284 if (wfd_sink->pipeline && wfd_sink->pipeline->videobin && wfd_sink->pipeline->videobin[WFD_SINK_V_SINK].gst)
1285 g_object_get(G_OBJECT(wfd_sink->pipeline->videobin[WFD_SINK_V_SINK].gst), "ts-offset", &ts_offset, NULL);
1288 render_time = GST_BUFFER_TIMESTAMP(buffer);
1289 render_time += ts_offset;
1291 /* chekc this buffer could be rendered or not */
1292 if (GST_CLOCK_TIME_IS_VALID(running_time) && GST_CLOCK_TIME_IS_VALID(render_time)) {
1293 diff = GST_CLOCK_DIFF(running_time, render_time);
1295 /* this buffer could be NOT rendered */
1296 wfd_sink_debug("%s : diff time : -%" GST_TIME_FORMAT "\n",
1297 GST_STR_NULL((GST_OBJECT_NAME(pad))),
1298 GST_TIME_ARGS(GST_CLOCK_DIFF(render_time, running_time)));
1300 /* this buffer could be rendered */
1301 /*wfd_sink_debug ("%s :diff time : %" GST_TIME_FORMAT "\n", */
1302 /* GST_STR_NULL((GST_OBJECT_NAME(pad))), */
1303 /* GST_TIME_ARGS(diff)); */
1307 /* update buffer count and gap */
1308 if (g_strrstr(GST_OBJECT_NAME(pad), "video")) {
1309 wfd_sink->video_buffer_count++;
1310 wfd_sink->video_accumulated_gap += diff;
1311 } else if (g_strrstr(GST_OBJECT_NAME(pad), "audio")) {
1312 wfd_sink->audio_buffer_count++;
1313 wfd_sink->audio_accumulated_gap += diff;
1315 wfd_sink_warning("invalid buffer type.. \n");
1316 return GST_PAD_PROBE_DROP;
1319 if (GST_CLOCK_TIME_IS_VALID(wfd_sink->last_buffer_timestamp)) {
1320 /* fisrt 60sec, just calculate the gap between source device and sink device */
1321 if (GST_BUFFER_TIMESTAMP(buffer) < 60 * GST_SECOND)
1322 return GST_PAD_PROBE_OK;
1324 /* every 10sec, calculate the gap between source device and sink device */
1325 if (GST_CLOCK_DIFF(wfd_sink->last_buffer_timestamp, GST_BUFFER_TIMESTAMP(buffer))
1326 > COMPENSATION_CHECK_PERIOD) {
1327 gint64 audio_avgrage_gap = 0LL;
1328 gint64 video_avgrage_gap = 0LL;
1329 gint64 audio_avgrage_gap_diff = 0LL;
1330 gint64 video_avgrage_gap_diff = 0LL;
1331 gboolean video_minus_compensation = FALSE;
1332 gboolean audio_minus_compensation = FALSE;
1333 gint64 avgrage_gap_diff = 0LL;
1334 gboolean minus_compensation = FALSE;
1337 if (wfd_sink->video_buffer_count > 0) {
1338 video_avgrage_gap = wfd_sink->video_accumulated_gap / wfd_sink->video_buffer_count;
1340 if (wfd_sink->video_average_gap != 0) {
1341 if (video_avgrage_gap > wfd_sink->video_average_gap) {
1342 video_avgrage_gap_diff = video_avgrage_gap - wfd_sink->video_average_gap;
1343 video_minus_compensation = TRUE;
1345 video_avgrage_gap_diff = wfd_sink->video_average_gap - video_avgrage_gap;
1346 video_minus_compensation = FALSE;
1349 wfd_sink_debug("first update video average gap(%lld) \n", video_avgrage_gap);
1350 wfd_sink->video_average_gap = video_avgrage_gap;
1353 wfd_sink_debug("there is no video buffer flow during %"GST_TIME_FORMAT
1354 " ~ %" GST_TIME_FORMAT"\n",
1355 GST_TIME_ARGS(wfd_sink->last_buffer_timestamp),
1356 GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(buffer)));
1360 if (wfd_sink->audio_buffer_count > 0) {
1361 audio_avgrage_gap = wfd_sink->audio_accumulated_gap / wfd_sink->audio_buffer_count;
1363 if (wfd_sink->audio_average_gap != 0) {
1364 if (audio_avgrage_gap > wfd_sink->audio_average_gap) {
1365 audio_avgrage_gap_diff = audio_avgrage_gap - wfd_sink->audio_average_gap;
1366 audio_minus_compensation = TRUE;
1368 audio_avgrage_gap_diff = wfd_sink->audio_average_gap - audio_avgrage_gap;
1369 audio_minus_compensation = FALSE;
1372 wfd_sink_debug("first update audio average gap(%lld) \n", audio_avgrage_gap);
1373 wfd_sink->audio_average_gap = audio_avgrage_gap;
1376 wfd_sink_debug("there is no audio buffer flow during %"GST_TIME_FORMAT
1377 " ~ %" GST_TIME_FORMAT"\n",
1378 GST_TIME_ARGS(wfd_sink->last_buffer_timestamp),
1379 GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(buffer)));
1382 /* selecet average_gap_diff between video and audio */
1383 /* which makes no buffer drop in the sink elements */
1384 if (video_avgrage_gap_diff && audio_avgrage_gap_diff) {
1385 if (!video_minus_compensation && !audio_minus_compensation) {
1386 minus_compensation = FALSE;
1387 if (video_avgrage_gap_diff > audio_avgrage_gap_diff)
1388 avgrage_gap_diff = video_avgrage_gap_diff;
1390 avgrage_gap_diff = audio_avgrage_gap_diff;
1391 } else if (video_minus_compensation && audio_minus_compensation) {
1392 minus_compensation = TRUE;
1393 if (video_avgrage_gap_diff > audio_avgrage_gap_diff)
1394 avgrage_gap_diff = audio_avgrage_gap_diff;
1396 avgrage_gap_diff = video_avgrage_gap_diff;
1398 minus_compensation = FALSE;
1399 if (!video_minus_compensation)
1400 avgrage_gap_diff = video_avgrage_gap_diff;
1402 avgrage_gap_diff = audio_avgrage_gap_diff;
1404 } else if (video_avgrage_gap_diff) {
1405 minus_compensation = video_minus_compensation;
1406 avgrage_gap_diff = video_avgrage_gap_diff;
1407 } else if (audio_avgrage_gap_diff) {
1408 minus_compensation = audio_minus_compensation;
1409 avgrage_gap_diff = audio_avgrage_gap_diff;
1412 wfd_sink_debug("average diff gap difference beween audio:%s%lld and video:%s%lld \n",
1413 audio_minus_compensation ? "-" : "", audio_avgrage_gap_diff,
1414 video_minus_compensation ? "-" : "", video_avgrage_gap_diff);
1417 /* if calculated gap diff is larger than 1ms. need to compensate buffer timestamp */
1418 if (avgrage_gap_diff >= COMPENSATION_CRETERIA_VALUE) {
1419 if (minus_compensation)
1420 ts_offset -= avgrage_gap_diff;
1422 ts_offset += avgrage_gap_diff;
1424 wfd_sink_debug("do timestamp compensation : %s%lld (ts-offset : %"
1425 GST_TIME_FORMAT") at(%" GST_TIME_FORMAT")\n",
1426 minus_compensation ? "-" : "", avgrage_gap_diff,
1427 GST_TIME_ARGS(ts_offset), GST_TIME_ARGS(running_time));
1429 if (wfd_sink->pipeline && wfd_sink->pipeline->audiobin && wfd_sink->pipeline->audiobin[WFD_SINK_A_SINK].gst)
1430 g_object_set(G_OBJECT(wfd_sink->pipeline->audiobin[WFD_SINK_A_SINK].gst), "ts-offset", (gint64)ts_offset, NULL);
1431 if (wfd_sink->pipeline && wfd_sink->pipeline->videobin && wfd_sink->pipeline->videobin[WFD_SINK_V_SINK].gst)
1432 g_object_set(G_OBJECT(wfd_sink->pipeline->videobin[WFD_SINK_V_SINK].gst), "ts-offset", (gint64)ts_offset, NULL);
1434 wfd_sink_debug("don't need to do timestamp compensation : %s%lld (ts-offset : %"GST_TIME_FORMAT ")\n",
1435 minus_compensation ? "-" : "", avgrage_gap_diff, GST_TIME_ARGS(ts_offset));
1439 wfd_sink->video_buffer_count = 0;
1440 wfd_sink->video_accumulated_gap = 0LL;
1441 wfd_sink->audio_buffer_count = 0;
1442 wfd_sink->audio_accumulated_gap = 0LL;
1443 wfd_sink->last_buffer_timestamp = GST_BUFFER_TIMESTAMP(buffer);
1446 wfd_sink_debug("first update last buffer timestamp :%" GST_TIME_FORMAT" \n",
1447 GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(buffer)));
1448 wfd_sink->last_buffer_timestamp = GST_BUFFER_TIMESTAMP(buffer);
1451 return GST_PAD_PROBE_OK;
1456 __mm_wfd_sink_demux_pad_added(GstElement *ele, GstPad *pad, gpointer data)
1458 mm_wfd_sink_t *wfd_sink = (mm_wfd_sink_t *)data;
1459 gchar *name = gst_pad_get_name(pad);
1460 GstElement *sinkbin = NULL;
1461 GstPad *sinkpad = NULL;
1463 wfd_sink_debug_fenter();
1465 wfd_sink_return_if_fail(wfd_sink && wfd_sink->pipeline);
1467 if (name[0] == 'v') {
1468 wfd_sink_debug("=========== >>>>>>>>>> Received VIDEO pad...\n");
1470 MMWFDSINK_PAD_PROBE(wfd_sink, pad, NULL, NULL);
1472 gst_pad_add_probe(pad,
1473 GST_PAD_PROBE_TYPE_BUFFER,
1474 _mm_wfd_sink_check_running_time,
1478 if (GST_STATE(wfd_sink->pipeline->videobin[WFD_SINK_V_BIN].gst) <= GST_STATE_NULL) {
1479 wfd_sink_debug("need to prepare videobin");
1480 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_videobin(wfd_sink)) {
1481 wfd_sink_error("failed to prepare videobin....\n");
1486 sinkbin = wfd_sink->pipeline->videobin[WFD_SINK_V_BIN].gst;
1488 wfd_sink->added_av_pad_num++;
1489 } else if (name[0] == 'a') {
1490 wfd_sink_debug("=========== >>>>>>>>>> Received AUDIO pad...\n");
1492 MMWFDSINK_PAD_PROBE(wfd_sink, pad, NULL, NULL);
1494 gst_pad_add_probe(pad,
1495 GST_PAD_PROBE_TYPE_BUFFER,
1496 _mm_wfd_sink_check_running_time,
1500 if (GST_STATE(wfd_sink->pipeline->audiobin[WFD_SINK_A_BIN].gst) <= GST_STATE_NULL) {
1501 wfd_sink_debug("need to prepare audiobin");
1502 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_audiobin(wfd_sink)) {
1503 wfd_sink_error("failed to prepare audiobin....\n");
1508 sinkbin = wfd_sink->pipeline->audiobin[WFD_SINK_A_BIN].gst;
1510 wfd_sink->added_av_pad_num++;
1512 wfd_sink_error("not handling.....\n\n\n");
1517 wfd_sink_debug("add %s to pipeline.\n",
1518 GST_STR_NULL(GST_ELEMENT_NAME(sinkbin)));
1521 if (!gst_bin_add(GST_BIN(wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst), sinkbin)) {
1522 wfd_sink_error("failed to add sinkbin to pipeline\n");
1526 wfd_sink_debug("link %s .\n", GST_STR_NULL(GST_ELEMENT_NAME(sinkbin)));
1529 sinkpad = gst_element_get_static_pad(GST_ELEMENT_CAST(sinkbin), "sink");
1531 wfd_sink_error("failed to get pad from sinkbin\n");
1535 if (GST_PAD_LINK_OK != gst_pad_link_full(pad, sinkpad, GST_PAD_LINK_CHECK_NOTHING)) {
1536 wfd_sink_error("failed to link sinkbin\n");
1540 wfd_sink_debug("sync state %s with pipeline .\n",
1541 GST_STR_NULL(GST_ELEMENT_NAME(sinkbin)));
1544 if (!gst_element_sync_state_with_parent(GST_ELEMENT_CAST(sinkbin))) {
1545 wfd_sink_error("failed to sync sinkbin state with parent \n");
1549 gst_object_unref(GST_OBJECT(sinkpad));
1554 if (wfd_sink->added_av_pad_num == 2) {
1555 wfd_sink_debug("whole pipeline is constructed. \n");
1557 /* generate dot file of the constructed pipeline of wifi display sink */
1558 MMWFDSINK_GENERATE_DOT_IF_ENABLED(wfd_sink, "constructed-pipeline");
1561 MMWFDSINK_FREEIF(name);
1563 wfd_sink_debug_fleave();
1569 MMWFDSINK_FREEIF(name);
1572 gst_object_unref(GST_OBJECT(sinkpad));
1575 /* need to notify to app */
1576 MMWFDSINK_POST_MESSAGE(wfd_sink,
1577 MM_ERROR_WFD_INTERNAL,
1578 MMWFDSINK_CURRENT_STATE(wfd_sink));
1584 __mm_wfd_sink_change_av_format(GstElement *wfdrtspsrc, gpointer *need_to_flush, gpointer data)
1586 mm_wfd_sink_t *wfd_sink = (mm_wfd_sink_t *)data;
1588 wfd_sink_debug_fenter();
1590 wfd_sink_return_if_fail(wfd_sink);
1591 wfd_sink_return_if_fail(need_to_flush);
1593 if (MMWFDSINK_CURRENT_STATE(wfd_sink) == MM_WFD_SINK_STATE_PLAYING) {
1594 wfd_sink_debug("need to flush pipeline");
1595 *need_to_flush = (gpointer) TRUE;
1597 wfd_sink_debug("don't need to flush pipeline");
1598 *need_to_flush = (gpointer) FALSE;
1602 wfd_sink_debug_fleave();
1607 __mm_wfd_sink_update_stream_info(GstElement *wfdrtspsrc, GstStructure *str, gpointer data)
1609 mm_wfd_sink_t *wfd_sink = (mm_wfd_sink_t *)data;
1610 WFDSinkManagerCMDType cmd = WFD_SINK_MANAGER_CMD_NONE;
1611 MMWFDSinkStreamInfo *stream_info = NULL;
1612 gint is_valid_audio_format = FALSE;
1613 gint is_valid_video_format = FALSE;
1614 gchar *audio_format;
1615 gchar *video_format;
1617 wfd_sink_debug_fenter();
1619 wfd_sink_return_if_fail(str && GST_IS_STRUCTURE(str));
1620 wfd_sink_return_if_fail(wfd_sink);
1622 stream_info = &wfd_sink->stream_info;
1624 if (gst_structure_has_field(str, "audio_format")) {
1625 is_valid_audio_format = TRUE;
1626 audio_format = g_strdup(gst_structure_get_string(str, "audio_format"));
1627 if (g_strrstr(audio_format, "AAC"))
1628 stream_info->audio_stream_info.codec = WFD_SINK_AUDIO_CODEC_AAC;
1629 else if (g_strrstr(audio_format, "AC3"))
1630 stream_info->audio_stream_info.codec = WFD_SINK_AUDIO_CODEC_AC3;
1631 else if (g_strrstr(audio_format, "LPCM"))
1632 stream_info->audio_stream_info.codec = WFD_SINK_AUDIO_CODEC_LPCM;
1634 wfd_sink_error("invalid audio format(%s)...\n", audio_format);
1635 is_valid_audio_format = FALSE;
1638 if (is_valid_audio_format == TRUE) {
1639 if (gst_structure_has_field(str, "audio_rate"))
1640 gst_structure_get_int(str, "audio_rate", &stream_info->audio_stream_info.sample_rate);
1641 if (gst_structure_has_field(str, "audio_channels"))
1642 gst_structure_get_int(str, "audio_channels", &stream_info->audio_stream_info.channels);
1643 if (gst_structure_has_field(str, "audio_bitwidth"))
1644 gst_structure_get_int(str, "audio_bitwidth", &stream_info->audio_stream_info.bitwidth);
1646 cmd = cmd | WFD_SINK_MANAGER_CMD_LINK_A_BIN;
1648 wfd_sink_debug("audio_format : %s \n \t rate : %d \n \t channels : %d \n \t bitwidth : %d \n \t \n",
1650 stream_info->audio_stream_info.sample_rate,
1651 stream_info->audio_stream_info.channels,
1652 stream_info->audio_stream_info.bitwidth);
1656 if (gst_structure_has_field(str, "video_format")) {
1657 is_valid_video_format = TRUE;
1658 video_format = g_strdup(gst_structure_get_string(str, "video_format"));
1659 if (!g_strrstr(video_format, "H264")) {
1660 wfd_sink_error("invalid video format(%s)...\n", video_format);
1661 is_valid_video_format = FALSE;
1664 if (is_valid_video_format == TRUE) {
1665 stream_info->video_stream_info.codec = WFD_SINK_VIDEO_CODEC_H264;
1667 if (gst_structure_has_field(str, "video_width"))
1668 gst_structure_get_int(str, "video_width", &stream_info->video_stream_info.width);
1669 if (gst_structure_has_field(str, "video_height"))
1670 gst_structure_get_int(str, "video_height", &stream_info->video_stream_info.height);
1671 if (gst_structure_has_field(str, "video_framerate"))
1672 gst_structure_get_int(str, "video_framerate", &stream_info->video_stream_info.frame_rate);
1674 cmd = cmd | WFD_SINK_MANAGER_CMD_LINK_V_BIN;
1676 wfd_sink_debug("video_format : %s \n \t width : %d \n \t height : %d \n \t frame_rate : %d \n \t \n",
1678 stream_info->video_stream_info.width,
1679 stream_info->video_stream_info.height,
1680 stream_info->video_stream_info.frame_rate);
1684 if (cmd != WFD_SINK_MANAGER_CMD_NONE) {
1685 WFD_SINK_MANAGER_LOCK(wfd_sink);
1686 WFD_SINK_MANAGER_SIGNAL_CMD(wfd_sink, cmd);
1687 WFD_SINK_MANAGER_UNLOCK(wfd_sink);
1690 wfd_sink_debug_fleave();
1693 static int __mm_wfd_sink_prepare_wfdrtspsrc(mm_wfd_sink_t *wfd_sink, GstElement *wfdrtspsrc)
1695 GstStructure *audio_param = NULL;
1696 GstStructure *video_param = NULL;
1697 GstStructure *hdcp_param = NULL;
1698 void *hdcp_handle = NULL;
1699 gint hdcp_version = 0;
1701 guint CEA_resolution = 0;
1702 guint VESA_resolution = 0;
1703 guint HH_resolution = 0;
1705 wfd_sink_debug_fenter();
1707 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
1708 wfd_sink_return_val_if_fail(wfd_sink->attrs, MM_ERROR_WFD_NOT_INITIALIZED);
1709 wfd_sink_return_val_if_fail(wfdrtspsrc, MM_ERROR_WFD_NOT_INITIALIZED);
1711 g_object_set(G_OBJECT(wfdrtspsrc), "debug", wfd_sink->ini.set_debug_property, NULL);
1712 g_object_set(G_OBJECT(wfdrtspsrc), "latency", wfd_sink->ini.jitter_buffer_latency, NULL);
1714 g_object_set(G_OBJECT(wfdrtspsrc), "do-request", wfd_sink->ini.enable_retransmission, NULL);
1716 g_object_set(G_OBJECT(wfdrtspsrc), "udp-buffer-size", 2097152, NULL);
1717 g_object_set(G_OBJECT(wfdrtspsrc), "enable-pad-probe", wfd_sink->ini.enable_wfdrtspsrc_pad_probe, NULL);
1719 audio_param = gst_structure_new("audio_param",
1720 "audio_codec", G_TYPE_UINT, wfd_sink->ini.audio_codec,
1721 "audio_latency", G_TYPE_UINT, wfd_sink->ini.audio_latency,
1722 "audio_channels", G_TYPE_UINT, wfd_sink->ini.audio_channel,
1723 "audio_sampling_frequency", G_TYPE_UINT, wfd_sink->ini.audio_sampling_frequency,
1726 CEA_resolution = wfd_sink->ini.video_cea_support;
1727 VESA_resolution = wfd_sink->ini.video_vesa_support;
1728 HH_resolution = wfd_sink->ini.video_hh_support;
1730 __mm_wfd_sink_prepare_video_resolution(wfd_sink->supportive_resolution,
1731 &CEA_resolution, &VESA_resolution, &HH_resolution);
1733 wfd_sink_debug("set video resolution CEA[%x] VESA[%x] HH[%x]", CEA_resolution, VESA_resolution, HH_resolution);
1735 video_param = gst_structure_new("video_param",
1736 "video_codec", G_TYPE_UINT, wfd_sink->ini.video_codec,
1737 "video_native_resolution", G_TYPE_UINT, wfd_sink->ini.video_native_resolution,
1738 "video_cea_support", G_TYPE_UINT, CEA_resolution,
1739 "video_vesa_support", G_TYPE_UINT, VESA_resolution,
1740 "video_hh_support", G_TYPE_UINT, HH_resolution,
1741 "video_profile", G_TYPE_UINT, wfd_sink->ini.video_profile,
1742 "video_level", G_TYPE_UINT, wfd_sink->ini.video_level,
1743 "video_latency", G_TYPE_UINT, wfd_sink->ini.video_latency,
1744 "video_vertical_resolution", G_TYPE_INT, wfd_sink->ini.video_vertical_resolution,
1745 "video_horizontal_resolution", G_TYPE_INT, wfd_sink->ini.video_horizontal_resolution,
1746 "video_minimum_slicing", G_TYPE_INT, wfd_sink->ini.video_minimum_slicing,
1747 "video_slice_enc_param", G_TYPE_INT, wfd_sink->ini.video_slice_enc_param,
1748 "video_framerate_control_support", G_TYPE_INT, wfd_sink->ini.video_framerate_control_support,
1751 mm_attrs_get_data_by_name(wfd_sink->attrs, "hdcp_handle", &hdcp_handle);
1752 mm_attrs_get_int_by_name(wfd_sink->attrs, "hdcp_version", &hdcp_version);
1753 mm_attrs_get_int_by_name(wfd_sink->attrs, "hdcp_port", &hdcp_port);
1754 wfd_sink_debug("set hdcp version %d with %d port\n", hdcp_version, hdcp_port);
1756 hdcp_param = gst_structure_new("hdcp_param",
1757 "hdcp_version", G_TYPE_INT, hdcp_version,
1758 "hdcp_port_no", G_TYPE_INT, hdcp_port,
1761 g_object_set(G_OBJECT(wfdrtspsrc), "audio-param", audio_param, NULL);
1762 g_object_set(G_OBJECT(wfdrtspsrc), "video-param", video_param, NULL);
1763 g_object_set(G_OBJECT(wfdrtspsrc), "hdcp-param", hdcp_param, NULL);
1765 g_signal_connect(wfdrtspsrc, "update-media-info",
1766 G_CALLBACK(__mm_wfd_sink_update_stream_info), wfd_sink);
1768 g_signal_connect(wfdrtspsrc, "change-av-format",
1769 G_CALLBACK(__mm_wfd_sink_change_av_format), wfd_sink);
1771 wfd_sink_debug_fleave();
1773 return MM_ERROR_NONE;
1776 static int __mm_wfd_sink_prepare_demux(mm_wfd_sink_t *wfd_sink, GstElement *demux)
1778 wfd_sink_debug_fenter();
1780 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
1781 wfd_sink_return_val_if_fail(demux, MM_ERROR_WFD_NOT_INITIALIZED);
1783 g_signal_connect(demux, "pad-added",
1784 G_CALLBACK(__mm_wfd_sink_demux_pad_added), wfd_sink);
1786 wfd_sink_debug_fleave();
1788 return MM_ERROR_NONE;
1791 static int __mm_wfd_sink_create_pipeline(mm_wfd_sink_t *wfd_sink)
1793 MMWFDSinkGstElement *mainbin = NULL;
1794 GList *element_bucket = NULL;
1798 wfd_sink_debug_fenter();
1800 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
1801 wfd_sink_return_val_if_fail(wfd_sink->attrs, MM_ERROR_WFD_NOT_INITIALIZED);
1803 /* Create pipeline */
1804 wfd_sink->pipeline = (MMWFDSinkGstPipelineInfo *) g_malloc0(sizeof(MMWFDSinkGstPipelineInfo));
1805 if (wfd_sink->pipeline == NULL)
1808 memset(wfd_sink->pipeline, 0, sizeof(MMWFDSinkGstPipelineInfo));
1810 /* create mainbin */
1811 mainbin = (MMWFDSinkGstElement *) g_malloc0(sizeof(MMWFDSinkGstElement) * WFD_SINK_M_NUM);
1812 if (mainbin == NULL)
1815 memset(mainbin, 0, sizeof(MMWFDSinkGstElement) * WFD_SINK_M_NUM);
1817 /* create pipeline */
1818 mainbin[WFD_SINK_M_PIPE].id = WFD_SINK_M_PIPE;
1819 mainbin[WFD_SINK_M_PIPE].gst = gst_pipeline_new("wfdsink");
1820 if (!mainbin[WFD_SINK_M_PIPE].gst) {
1821 wfd_sink_error("failed to create pipeline\n");
1825 /* create wfdrtspsrc */
1826 MMWFDSINK_CREATE_ELEMENT(mainbin, WFD_SINK_M_SRC, "wfdrtspsrc", "wfdsink_source", TRUE);
1827 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_SRC].gst, "src");
1828 if (mainbin[WFD_SINK_M_SRC].gst) {
1829 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_wfdrtspsrc(wfd_sink, mainbin[WFD_SINK_M_SRC].gst)) {
1830 wfd_sink_error("failed to prepare wfdrtspsrc...\n");
1835 /* create rtpmp2tdepay */
1836 MMWFDSINK_CREATE_ELEMENT(mainbin, WFD_SINK_M_DEPAY, "rtpmp2tdepay", "wfdsink_depay", TRUE);
1837 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_DEPAY].gst, "src");
1838 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_DEPAY].gst, "sink");
1840 MMWFDSINK_TS_DATA_DUMP(wfd_sink, mainbin[WFD_SINK_M_DEPAY].gst, "src");
1842 /* create tsdemuxer*/
1843 MMWFDSINK_CREATE_ELEMENT(mainbin, WFD_SINK_M_DEMUX, wfd_sink->ini.name_of_tsdemux, "wfdsink_demux", TRUE);
1844 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, mainbin[WFD_SINK_M_DEMUX].gst, "sink");
1845 if (mainbin[WFD_SINK_M_DEMUX].gst) {
1846 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_demux(wfd_sink, mainbin[WFD_SINK_M_DEMUX].gst)) {
1847 wfd_sink_error("failed to prepare demux...\n");
1852 /* adding created elements to pipeline */
1853 if (!__mm_wfd_sink_gst_element_add_bucket_to_bin(GST_BIN_CAST(mainbin[WFD_SINK_M_PIPE].gst), element_bucket, FALSE)) {
1854 wfd_sink_error("failed to add elements\n");
1858 /* linking elements in the bucket by added order. */
1859 if (__mm_wfd_sink_gst_element_link_bucket(element_bucket) == -1) {
1860 wfd_sink_error("failed to link elements\n");
1864 /* connect bus callback */
1865 bus = gst_pipeline_get_bus(GST_PIPELINE(mainbin[WFD_SINK_M_PIPE].gst));
1867 wfd_sink_error("cannot get bus from pipeline.\n");
1871 /* add bus message callback*/
1872 gst_bus_add_watch(bus, (GstBusFunc)_mm_wfd_sink_msg_callback, wfd_sink);
1874 /* set sync handler to get tag synchronously */
1875 gst_bus_set_sync_handler(bus, _mm_wfd_bus_sync_callback, wfd_sink, NULL);
1877 g_list_free(element_bucket);
1878 gst_object_unref(GST_OBJECT(bus));
1880 /* now we have completed mainbin. take it */
1881 wfd_sink->pipeline->mainbin = mainbin;
1883 wfd_sink_debug_fleave();
1885 return MM_ERROR_NONE;
1889 wfd_sink_error("ERROR : releasing pipeline\n");
1892 g_list_free(element_bucket);
1893 element_bucket = NULL;
1897 gst_object_unref(GST_OBJECT(bus));
1900 /* release element which are not added to bin */
1901 for (i = 1; i < WFD_SINK_M_NUM; i++) { /* NOTE : skip pipeline */
1902 if (mainbin != NULL && mainbin[i].gst) {
1903 GstObject *parent = NULL;
1904 parent = gst_element_get_parent(mainbin[i].gst);
1907 gst_object_unref(GST_OBJECT(mainbin[i].gst));
1908 mainbin[i].gst = NULL;
1910 gst_object_unref(GST_OBJECT(parent));
1915 /* release audiobin with it's childs */
1916 if (mainbin != NULL && mainbin[WFD_SINK_M_PIPE].gst)
1917 gst_object_unref(GST_OBJECT(mainbin[WFD_SINK_M_PIPE].gst));
1919 MMWFDSINK_FREEIF(mainbin);
1921 MMWFDSINK_FREEIF(wfd_sink->pipeline);
1923 return MM_ERROR_WFD_INTERNAL;
1926 int __mm_wfd_sink_link_audiobin(mm_wfd_sink_t *wfd_sink)
1928 MMWFDSinkGstElement *audiobin = NULL;
1929 MMWFDSinkGstElement *first_element = NULL;
1930 MMWFDSinkGstElement *last_element = NULL;
1931 gint audio_codec = WFD_SINK_AUDIO_CODEC_NONE;
1932 GList *element_bucket = NULL;
1933 GstPad *sinkpad = NULL;
1934 GstPad *srcpad = NULL;
1936 wfd_sink_debug_fenter();
1938 wfd_sink_return_val_if_fail(wfd_sink &&
1939 wfd_sink->pipeline &&
1940 wfd_sink->pipeline->audiobin &&
1941 wfd_sink->pipeline->audiobin[WFD_SINK_A_BIN].gst,
1942 MM_ERROR_WFD_NOT_INITIALIZED);
1943 wfd_sink_return_val_if_fail(wfd_sink->prev_audio_dec_src_pad &&
1944 wfd_sink->next_audio_dec_sink_pad,
1945 MM_ERROR_WFD_INTERNAL);
1947 if (wfd_sink->audio_bin_is_linked) {
1948 wfd_sink_debug("audiobin is already linked... nothing to do\n");
1949 return MM_ERROR_NONE;
1953 audiobin = wfd_sink->pipeline->audiobin;
1955 /* check audio codec */
1956 audio_codec = wfd_sink->stream_info.audio_stream_info.codec;
1957 switch (audio_codec) {
1958 case WFD_SINK_AUDIO_CODEC_LPCM:
1959 if (audiobin[WFD_SINK_A_LPCM_CONVERTER].gst)
1960 element_bucket = g_list_append(element_bucket, &audiobin[WFD_SINK_A_LPCM_CONVERTER]);
1961 if (audiobin[WFD_SINK_A_LPCM_FILTER].gst)
1962 element_bucket = g_list_append(element_bucket, &audiobin[WFD_SINK_A_LPCM_FILTER]);
1965 case WFD_SINK_AUDIO_CODEC_AAC:
1966 if (audiobin[WFD_SINK_A_AAC_PARSE].gst)
1967 element_bucket = g_list_append(element_bucket, &audiobin[WFD_SINK_A_AAC_PARSE]);
1968 if (audiobin[WFD_SINK_A_AAC_DEC].gst)
1969 element_bucket = g_list_append(element_bucket, &audiobin[WFD_SINK_A_AAC_DEC]);
1972 case WFD_SINK_AUDIO_CODEC_AC3:
1973 if (audiobin[WFD_SINK_A_AC3_PARSE].gst)
1974 element_bucket = g_list_append(element_bucket, &audiobin[WFD_SINK_A_AC3_PARSE]);
1975 if (audiobin[WFD_SINK_A_AC3_DEC].gst)
1976 element_bucket = g_list_append(element_bucket, &audiobin[WFD_SINK_A_AC3_DEC]);
1980 wfd_sink_error("audio type is not decied yet. cannot link audio decoder...\n");
1981 return MM_ERROR_WFD_INTERNAL;
1985 if (!element_bucket) {
1986 wfd_sink_debug("there is no additional elements to be linked... just link audiobin.\n");
1987 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)) {
1988 wfd_sink_error("failed to link audiobin....\n");
1994 /* adding elements to audiobin */
1995 if (!__mm_wfd_sink_gst_element_add_bucket_to_bin(GST_BIN_CAST(audiobin[WFD_SINK_A_BIN].gst), element_bucket, FALSE)) {
1996 wfd_sink_error("failed to add elements to audiobin\n");
2000 /* linking elements in the bucket by added order. */
2001 if (__mm_wfd_sink_gst_element_link_bucket(element_bucket) == -1) {
2002 wfd_sink_error("failed to link elements\n");
2007 first_element = (MMWFDSinkGstElement *)g_list_nth_data(element_bucket, 0);
2008 if (!first_element) {
2009 wfd_sink_error("failed to get first element to be linked....\n");
2013 sinkpad = gst_element_get_static_pad(GST_ELEMENT(first_element->gst), "sink");
2015 wfd_sink_error("failed to get sink pad from element(%s)\n", GST_ELEMENT_NAME(first_element->gst));
2019 if (GST_PAD_LINK_OK != gst_pad_link_full(wfd_sink->prev_audio_dec_src_pad, sinkpad, GST_PAD_LINK_CHECK_NOTHING)) {
2020 wfd_sink_error("failed to link audiobin....\n");
2024 gst_object_unref(GST_OBJECT(sinkpad));
2029 last_element = (MMWFDSinkGstElement *)g_list_nth_data(element_bucket, g_list_length(element_bucket) - 1);
2030 if (!last_element) {
2031 wfd_sink_error("failed to get last element to be linked....\n");
2035 srcpad = gst_element_get_static_pad(GST_ELEMENT(last_element->gst), "src");
2037 wfd_sink_error("failed to get src pad from element(%s)\n", GST_ELEMENT_NAME(last_element->gst));
2041 if (GST_PAD_LINK_OK != gst_pad_link_full(srcpad, wfd_sink->next_audio_dec_sink_pad, GST_PAD_LINK_CHECK_NOTHING)) {
2042 wfd_sink_error("failed to link audiobin....\n");
2046 gst_object_unref(GST_OBJECT(srcpad));
2049 g_list_free(element_bucket);
2052 wfd_sink->audio_bin_is_linked = TRUE;
2054 wfd_sink_debug_fleave();
2056 return MM_ERROR_NONE;
2061 gst_object_unref(GST_OBJECT(srcpad));
2065 gst_object_unref(GST_OBJECT(sinkpad));
2068 g_list_free(element_bucket);
2070 return MM_ERROR_WFD_INTERNAL;
2073 static int __mm_wfd_sink_prepare_audiosink(mm_wfd_sink_t *wfd_sink, GstElement *audio_sink)
2075 wfd_sink_debug_fenter();
2077 /* check audiosink is created */
2078 wfd_sink_return_val_if_fail(audio_sink, MM_ERROR_WFD_INVALID_ARGUMENT);
2079 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
2081 g_object_set(G_OBJECT(audio_sink), "provide-clock", FALSE, NULL);
2082 g_object_set(G_OBJECT(audio_sink), "buffer-time", 100000LL, NULL);
2083 g_object_set(G_OBJECT(audio_sink), "query-position-support", FALSE, NULL);
2084 g_object_set(G_OBJECT(audio_sink), "slave-method", 2, NULL);
2085 g_object_set(G_OBJECT(audio_sink), "async", wfd_sink->ini.audio_sink_async, NULL);
2086 g_object_set(G_OBJECT(audio_sink), "ts-offset", (gint64)wfd_sink->ini.sink_ts_offset, NULL);
2088 wfd_sink_debug_fleave();
2090 return MM_ERROR_NONE;
2094 __mm_wfd_sink_queue_overrun(GstElement *element, gpointer u_data)
2098 return_if_fail(element);
2100 wfd_sink_warning("%s is overrun\n",
2101 GST_STR_NULL(GST_ELEMENT_NAME(element)));
2108 static int __mm_wfd_sink_destroy_audiobin(mm_wfd_sink_t *wfd_sink)
2110 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
2111 MMWFDSinkGstElement *audiobin = NULL;
2112 GstObject *parent = NULL;
2115 wfd_sink_debug_fenter();
2117 wfd_sink_return_val_if_fail(wfd_sink,
2118 MM_ERROR_WFD_NOT_INITIALIZED);
2120 if (wfd_sink->pipeline &&
2121 wfd_sink->pipeline->audiobin &&
2122 wfd_sink->pipeline->audiobin[WFD_SINK_A_BIN].gst) {
2123 audiobin = wfd_sink->pipeline->audiobin;
2125 wfd_sink_debug("audiobin is not created, nothing to destroy\n");
2126 return MM_ERROR_NONE;
2130 parent = gst_element_get_parent(audiobin[WFD_SINK_A_BIN].gst);
2132 wfd_sink_debug("audiobin has no parent.. need to relase by itself\n");
2134 if (GST_STATE(audiobin[WFD_SINK_A_BIN].gst) >= GST_STATE_READY) {
2135 wfd_sink_debug("try to change state of audiobin to NULL\n");
2136 ret = gst_element_set_state(audiobin[WFD_SINK_A_BIN].gst, GST_STATE_NULL);
2137 if (ret != GST_STATE_CHANGE_SUCCESS) {
2138 wfd_sink_error("failed to change state of audiobin to NULL\n");
2139 return MM_ERROR_WFD_INTERNAL;
2143 /* release element which are not added to bin */
2144 for (i = 1; i < WFD_SINK_A_NUM; i++) { /* NOTE : skip bin */
2145 if (audiobin[i].gst) {
2146 parent = gst_element_get_parent(audiobin[i].gst);
2148 wfd_sink_debug("unref %s(current ref %d)\n",
2149 GST_STR_NULL(GST_ELEMENT_NAME(audiobin[i].gst)),
2150 ((GObject *) audiobin[i].gst)->ref_count);
2151 gst_object_unref(GST_OBJECT(audiobin[i].gst));
2152 audiobin[i].gst = NULL;
2154 wfd_sink_debug("unref %s(current ref %d)\n",
2155 GST_STR_NULL(GST_ELEMENT_NAME(audiobin[i].gst)),
2156 ((GObject *) audiobin[i].gst)->ref_count);
2157 gst_object_unref(GST_OBJECT(parent));
2162 /* release audiobin with it's childs */
2163 if (audiobin[WFD_SINK_A_BIN].gst)
2164 gst_object_unref(GST_OBJECT(audiobin[WFD_SINK_A_BIN].gst));
2167 wfd_sink_debug("audiobin has parent(%s), unref it \n",
2168 GST_STR_NULL(GST_OBJECT_NAME(GST_OBJECT(parent))));
2170 gst_object_unref(GST_OBJECT(parent));
2173 wfd_sink_debug_fleave();
2175 return MM_ERROR_NONE;
2178 static int __mm_wfd_sink_create_audiobin(mm_wfd_sink_t *wfd_sink)
2180 MMWFDSinkGstElement *audiobin = NULL;
2181 gint audio_codec = WFD_SINK_AUDIO_CODEC_NONE;
2182 gboolean link_audio_dec = TRUE;
2183 GList *element_bucket = NULL;
2185 GstPad *ghostpad = NULL;
2186 GstCaps *caps = NULL;
2188 wfd_sink_debug_fenter();
2190 wfd_sink_return_val_if_fail(wfd_sink &&
2191 wfd_sink->pipeline &&
2192 wfd_sink->pipeline->mainbin,
2193 MM_ERROR_WFD_NOT_INITIALIZED);
2196 audiobin = (MMWFDSinkGstElement *)g_malloc0(sizeof(MMWFDSinkGstElement) * WFD_SINK_A_NUM);
2198 wfd_sink_error("failed to allocate memory for audiobin\n");
2199 return MM_ERROR_WFD_NO_FREE_SPACE;
2202 /* create audiobin */
2203 audiobin[WFD_SINK_A_BIN].id = WFD_SINK_A_BIN;
2204 audiobin[WFD_SINK_A_BIN].gst = gst_bin_new("audiobin");
2205 if (!audiobin[WFD_SINK_A_BIN].gst) {
2206 wfd_sink_error("failed to create audiobin\n");
2210 /* check audio decoder could be linked or not */
2211 switch (wfd_sink->stream_info.audio_stream_info.codec) {
2212 case WFD_SINK_AUDIO_CODEC_AAC:
2213 audio_codec = WFD_AUDIO_AAC;
2215 case WFD_SINK_AUDIO_CODEC_AC3:
2216 audio_codec = WFD_AUDIO_AC3;
2218 case WFD_SINK_AUDIO_CODEC_LPCM:
2219 audio_codec = WFD_AUDIO_LPCM;
2221 case WFD_SINK_AUDIO_CODEC_NONE:
2223 wfd_sink_debug("audio decoder could NOT be linked now, just prepare.\n");
2224 audio_codec = wfd_sink->ini.audio_codec;
2225 link_audio_dec = FALSE;
2229 /* set need to link audio decoder flag*/
2230 wfd_sink->audio_bin_is_linked = link_audio_dec;
2232 /* queue - drm - parse - dec/capsfilter - audioconvert- volume - sink */
2234 MMWFDSINK_CREATE_ELEMENT(audiobin, WFD_SINK_A_QUEUE, "queue", "audio_queue", link_audio_dec);
2235 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_QUEUE].gst, "sink");
2236 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_QUEUE].gst, "src");
2237 if (audiobin[WFD_SINK_A_QUEUE].gst) {
2238 g_object_set(G_OBJECT(audiobin[WFD_SINK_A_QUEUE].gst), "max-size-bytes", 0, NULL);
2239 g_object_set(G_OBJECT(audiobin[WFD_SINK_A_QUEUE].gst), "max-size-buffers", 0, NULL);
2240 g_object_set(G_OBJECT(audiobin[WFD_SINK_A_QUEUE].gst), "max-size-time", (guint64)3000000000ULL, NULL);
2241 g_signal_connect(audiobin[WFD_SINK_A_QUEUE].gst, "overrun",
2242 G_CALLBACK(__mm_wfd_sink_queue_overrun), wfd_sink);
2244 if (!link_audio_dec) {
2245 if (!gst_bin_add(GST_BIN_CAST(audiobin[WFD_SINK_A_BIN].gst), audiobin[WFD_SINK_A_QUEUE].gst)) {
2246 wfd_sink_error("failed to add %s to audiobin\n",
2247 GST_STR_NULL(GST_ELEMENT_NAME(audiobin[WFD_SINK_A_QUEUE].gst)));
2251 if (audiobin[WFD_SINK_A_HDCP].gst) {
2252 if (!gst_bin_add(GST_BIN_CAST(audiobin[WFD_SINK_A_BIN].gst), audiobin[WFD_SINK_A_HDCP].gst)) {
2253 wfd_sink_error("failed to add %s to audiobin\n",
2254 GST_STR_NULL(GST_ELEMENT_NAME(audiobin[WFD_SINK_A_HDCP].gst)));
2258 if (!gst_element_link(audiobin[WFD_SINK_A_QUEUE].gst, audiobin[WFD_SINK_A_HDCP].gst)) {
2259 wfd_sink_error("failed to link [%s] to [%s] success\n",
2260 GST_STR_NULL(GST_ELEMENT_NAME(audiobin[WFD_SINK_A_QUEUE].gst)),
2261 GST_STR_NULL(GST_ELEMENT_NAME(audiobin[WFD_SINK_A_HDCP].gst)));
2265 wfd_sink->prev_audio_dec_src_pad = gst_element_get_static_pad(audiobin[WFD_SINK_A_HDCP].gst, "src");
2267 wfd_sink->prev_audio_dec_src_pad = gst_element_get_static_pad(audiobin[WFD_SINK_A_QUEUE].gst, "src");
2270 if (!wfd_sink->prev_audio_dec_src_pad) {
2271 wfd_sink_error("failed to get src pad from previous element of audio decoder\n");
2275 wfd_sink_debug("take src pad from previous element of audio decoder for linking\n");
2278 if (audio_codec & WFD_AUDIO_LPCM) {
2279 /* create LPCM converter */
2280 MMWFDSINK_CREATE_ELEMENT(audiobin, WFD_SINK_A_LPCM_CONVERTER, wfd_sink->ini.name_of_lpcm_converter, "audio_lpcm_convert", link_audio_dec);
2281 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_LPCM_CONVERTER].gst, "sink");
2282 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_LPCM_CONVERTER].gst, "src");
2284 /* create LPCM filter */
2285 MMWFDSINK_CREATE_ELEMENT(audiobin, WFD_SINK_A_LPCM_FILTER, wfd_sink->ini.name_of_lpcm_filter, "audio_lpcm_filter", link_audio_dec);
2286 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_LPCM_FILTER].gst, "sink");
2287 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_LPCM_FILTER].gst, "src");
2288 if (audiobin[WFD_SINK_A_LPCM_FILTER].gst) {
2289 caps = gst_caps_new_simple("audio/x-raw",
2290 "rate", G_TYPE_INT, 48000,
2291 "channels", G_TYPE_INT, 2,
2292 "format", G_TYPE_STRING, "S16LE", NULL);
2294 g_object_set(G_OBJECT(audiobin[WFD_SINK_A_LPCM_FILTER].gst), "caps", caps, NULL);
2295 gst_object_unref(GST_OBJECT(caps));
2299 if (audio_codec & WFD_AUDIO_AAC) {
2300 /* create AAC parse */
2301 MMWFDSINK_CREATE_ELEMENT(audiobin, WFD_SINK_A_AAC_PARSE, wfd_sink->ini.name_of_aac_parser, "audio_aac_parser", link_audio_dec);
2302 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_AAC_PARSE].gst, "sink");
2303 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_AAC_PARSE].gst, "src");
2305 /* create AAC decoder */
2306 MMWFDSINK_CREATE_ELEMENT(audiobin, WFD_SINK_A_AAC_DEC, wfd_sink->ini.name_of_aac_decoder, "audio_aac_dec", link_audio_dec);
2307 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_AAC_DEC].gst, "sink");
2308 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_AAC_DEC].gst, "src");
2311 if (audio_codec & WFD_AUDIO_AC3) {
2312 /* create AC3 parser */
2313 MMWFDSINK_CREATE_ELEMENT(audiobin, WFD_SINK_A_AC3_PARSE, wfd_sink->ini.name_of_ac3_parser, "audio_ac3_parser", link_audio_dec);
2314 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_AC3_PARSE].gst, "sink");
2315 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_AC3_PARSE].gst, "src");
2317 /* create AC3 decoder */
2318 MMWFDSINK_CREATE_ELEMENT(audiobin, WFD_SINK_A_AC3_DEC, wfd_sink->ini.name_of_ac3_decoder, "audio_ac3_dec", link_audio_dec);
2319 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_AC3_DEC].gst, "sink");
2320 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_AC3_DEC].gst, "src");
2323 /* create resampler */
2324 MMWFDSINK_CREATE_ELEMENT(audiobin, WFD_SINK_A_RESAMPLER, wfd_sink->ini.name_of_audio_resampler, "audio_resampler", TRUE);
2325 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_RESAMPLER].gst, "sink");
2326 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_RESAMPLER].gst, "src");
2329 MMWFDSINK_CREATE_ELEMENT(audiobin, WFD_SINK_A_VOLUME, wfd_sink->ini.name_of_audio_volume, "audio_volume", TRUE);
2330 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_VOLUME].gst, "sink");
2331 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_VOLUME].gst, "src");
2333 /*TODO gstreamer-1.0 alsasink does not want process not S16LE format. */
2334 MMWFDSINK_CREATE_ELEMENT(audiobin, WFD_SINK_A_CAPSFILTER, "capsfilter", "audio_capsfilter", TRUE);
2335 if (audiobin[WFD_SINK_A_CAPSFILTER].gst) {
2336 caps = gst_caps_from_string("audio/x-raw, format=(string)S16LE");
2337 g_object_set(G_OBJECT(audiobin[WFD_SINK_A_CAPSFILTER].gst), "caps", caps, NULL);
2338 gst_caps_unref(caps);
2342 MMWFDSINK_CREATE_ELEMENT(audiobin, WFD_SINK_A_SINK, wfd_sink->ini.name_of_audio_sink, "audio_sink", TRUE);
2343 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, audiobin[WFD_SINK_A_SINK].gst, "sink");
2344 if (audiobin[WFD_SINK_A_SINK].gst) {
2345 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_audiosink(wfd_sink, audiobin[WFD_SINK_A_SINK].gst)) {
2346 wfd_sink_error("failed to set audio sink property....\n");
2351 if (!link_audio_dec) {
2352 MMWFDSinkGstElement *first_element = NULL;
2354 first_element = (MMWFDSinkGstElement *)g_list_nth_data(element_bucket, 0);
2355 if (!first_element) {
2356 wfd_sink_error("failed to get first element\n");
2360 wfd_sink->next_audio_dec_sink_pad = gst_element_get_static_pad(GST_ELEMENT(first_element->gst), "sink");
2361 if (!wfd_sink->next_audio_dec_sink_pad) {
2362 wfd_sink_error("failed to get sink pad from next element of audio decoder\n");
2366 wfd_sink_debug("take sink pad from next element of audio decoder for linking\n");
2369 /* adding created elements to audiobin */
2370 if (!__mm_wfd_sink_gst_element_add_bucket_to_bin(GST_BIN_CAST(audiobin[WFD_SINK_A_BIN].gst), element_bucket, FALSE)) {
2371 wfd_sink_error("failed to add elements\n");
2375 /* linking elements in the bucket by added order. */
2376 if (__mm_wfd_sink_gst_element_link_bucket(element_bucket) == -1) {
2377 wfd_sink_error("failed to link elements\n");
2381 /* get queue's sinkpad for creating ghostpad */
2382 pad = gst_element_get_static_pad(audiobin[WFD_SINK_A_QUEUE].gst, "sink");
2384 wfd_sink_error("failed to get pad from queue of audiobin\n");
2388 ghostpad = gst_ghost_pad_new("sink", pad);
2390 wfd_sink_error("failed to create ghostpad\n");
2394 if (FALSE == gst_element_add_pad(audiobin[WFD_SINK_A_BIN].gst, ghostpad)) {
2395 wfd_sink_error("failed to add ghostpad to audiobin\n");
2399 gst_object_unref(GST_OBJECT(pad));
2401 g_list_free(element_bucket);
2404 wfd_sink->pipeline->audiobin = audiobin;
2406 wfd_sink_debug_fleave();
2408 return MM_ERROR_NONE;
2411 wfd_sink_error("failed to create audiobin, releasing all\n");
2413 if (wfd_sink->next_audio_dec_sink_pad)
2414 gst_object_unref(GST_OBJECT(wfd_sink->next_audio_dec_sink_pad));
2415 wfd_sink->next_audio_dec_sink_pad = NULL;
2417 if (wfd_sink->prev_audio_dec_src_pad)
2418 gst_object_unref(GST_OBJECT(wfd_sink->prev_audio_dec_src_pad));
2419 wfd_sink->prev_audio_dec_src_pad = NULL;
2422 gst_object_unref(GST_OBJECT(pad));
2426 gst_object_unref(GST_OBJECT(ghostpad));
2430 g_list_free(element_bucket);
2431 element_bucket = NULL;
2433 /* release element which are not added to bin */
2434 __mm_wfd_sink_destroy_audiobin(wfd_sink);
2436 MMWFDSINK_FREEIF(audiobin);
2438 return MM_ERROR_WFD_INTERNAL;
2441 static int __mm_wfd_sink_prepare_videodec(mm_wfd_sink_t *wfd_sink, GstElement *video_dec)
2443 wfd_sink_debug_fenter();
2445 /* check video decoder is created */
2446 wfd_sink_return_val_if_fail(video_dec, MM_ERROR_WFD_INVALID_ARGUMENT);
2447 wfd_sink_return_val_if_fail(wfd_sink && wfd_sink->attrs, MM_ERROR_WFD_NOT_INITIALIZED);
2449 g_object_set(G_OBJECT(video_dec), "error-concealment", TRUE, NULL);
2451 wfd_sink_debug_fleave();
2453 return MM_ERROR_NONE;
2456 static int __mm_wfd_sink_prepare_videosink(mm_wfd_sink_t *wfd_sink, GstElement *video_sink)
2458 gboolean visible = TRUE;
2459 gint surface_type = MM_DISPLAY_SURFACE_X;
2462 wfd_sink_debug_fenter();
2464 /* check videosink is created */
2465 wfd_sink_return_val_if_fail(video_sink, MM_ERROR_WFD_INVALID_ARGUMENT);
2466 wfd_sink_return_val_if_fail(wfd_sink && wfd_sink->attrs, MM_ERROR_WFD_NOT_INITIALIZED);
2468 /* update display surface */
2469 /* mm_attrs_get_int_by_name(wfd_sink->attrs, "display_surface_type", &surface_type); */
2470 wfd_sink_debug("check display surface type attribute: %d", surface_type);
2471 mm_attrs_get_int_by_name(wfd_sink->attrs, "display_visible", &visible);
2472 wfd_sink_debug("check display visible attribute: %d", visible);
2474 /* configuring display */
2475 switch (surface_type) {
2476 case MM_DISPLAY_SURFACE_EVAS: {
2477 void *object = NULL;
2480 /* common case if using evas surface */
2481 mm_attrs_get_data_by_name(wfd_sink->attrs, "display_overlay", &object);
2482 mm_attrs_get_int_by_name(wfd_sink->attrs, "display_evas_do_scaling", &scaling);
2484 wfd_sink_debug("set video param : evas-object %x", object);
2485 g_object_set(G_OBJECT(video_sink), "evas-object", object, NULL);
2487 wfd_sink_error("no evas object");
2488 return MM_ERROR_WFD_INTERNAL;
2493 case MM_DISPLAY_SURFACE_X: {
2497 mm_attrs_get_data_by_name(wfd_sink->attrs, "display_overlay", (void **)&object);
2500 wfd_sink_debug("xid = %lu", xid);
2501 gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(video_sink), xid);
2503 wfd_sink_warning("Handle is NULL. Set xid as 0.. but, it's not recommended.");
2504 gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(video_sink), 0);
2509 case MM_DISPLAY_SURFACE_NULL: {
2511 wfd_sink_error("Not Supported Surface.");
2512 return MM_ERROR_WFD_INTERNAL;
2517 g_object_set(G_OBJECT(video_sink), "qos", FALSE, NULL);
2518 g_object_set(G_OBJECT(video_sink), "async", wfd_sink->ini.video_sink_async, NULL);
2519 g_object_set(G_OBJECT(video_sink), "max-lateness", (gint64)wfd_sink->ini.video_sink_max_lateness, NULL);
2520 g_object_set(G_OBJECT(video_sink), "visible", visible, NULL);
2521 g_object_set(G_OBJECT(video_sink), "ts-offset", (gint64)(wfd_sink->ini.sink_ts_offset), NULL);
2523 wfd_sink_debug_fleave();
2525 return MM_ERROR_NONE;
2528 static int __mm_wfd_sink_destroy_videobin(mm_wfd_sink_t *wfd_sink)
2530 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
2531 MMWFDSinkGstElement *videobin = NULL;
2532 GstObject *parent = NULL;
2535 wfd_sink_debug_fenter();
2537 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
2539 if (wfd_sink->pipeline &&
2540 wfd_sink->pipeline->videobin &&
2541 wfd_sink->pipeline->videobin[WFD_SINK_V_BIN].gst) {
2542 videobin = wfd_sink->pipeline->videobin;
2544 wfd_sink_debug("videobin is not created, nothing to destroy\n");
2545 return MM_ERROR_NONE;
2549 parent = gst_element_get_parent(videobin[WFD_SINK_V_BIN].gst);
2551 wfd_sink_debug("videobin has no parent.. need to relase by itself\n");
2553 if (GST_STATE(videobin[WFD_SINK_V_BIN].gst) >= GST_STATE_READY) {
2554 wfd_sink_debug("try to change state of videobin to NULL\n");
2555 ret = gst_element_set_state(videobin[WFD_SINK_V_BIN].gst, GST_STATE_NULL);
2556 if (ret != GST_STATE_CHANGE_SUCCESS) {
2557 wfd_sink_error("failed to change state of videobin to NULL\n");
2558 return MM_ERROR_WFD_INTERNAL;
2561 /* release element which are not added to bin */
2562 for (i = 1; i < WFD_SINK_V_NUM; i++) { /* NOTE : skip bin */
2563 if (videobin[i].gst) {
2564 parent = gst_element_get_parent(videobin[i].gst);
2566 wfd_sink_debug("unref %s(current ref %d)\n",
2567 GST_STR_NULL(GST_ELEMENT_NAME(videobin[i].gst)),
2568 ((GObject *) videobin[i].gst)->ref_count);
2569 gst_object_unref(GST_OBJECT(videobin[i].gst));
2570 videobin[i].gst = NULL;
2572 wfd_sink_debug("unref %s(current ref %d)\n",
2573 GST_STR_NULL(GST_ELEMENT_NAME(videobin[i].gst)),
2574 ((GObject *) videobin[i].gst)->ref_count);
2575 gst_object_unref(GST_OBJECT(parent));
2579 /* release audiobin with it's childs */
2580 if (videobin[WFD_SINK_V_BIN].gst) {
2581 gst_object_unref(GST_OBJECT(videobin[WFD_SINK_V_BIN].gst));
2584 wfd_sink_debug("videobin has parent(%s), unref it \n",
2585 GST_STR_NULL(GST_OBJECT_NAME(GST_OBJECT(parent))));
2587 gst_object_unref(GST_OBJECT(parent));
2590 wfd_sink_debug_fleave();
2592 return MM_ERROR_NONE;
2596 static int __mm_wfd_sink_create_videobin(mm_wfd_sink_t *wfd_sink)
2598 MMWFDSinkGstElement *first_element = NULL;
2599 MMWFDSinkGstElement *videobin = NULL;
2600 GList *element_bucket = NULL;
2602 GstPad *ghostpad = NULL;
2604 wfd_sink_debug_fenter();
2606 wfd_sink_return_val_if_fail(wfd_sink &&
2607 wfd_sink->pipeline &&
2608 wfd_sink->pipeline->mainbin,
2609 MM_ERROR_WFD_NOT_INITIALIZED);
2612 videobin = (MMWFDSinkGstElement *)g_malloc0(sizeof(MMWFDSinkGstElement) * WFD_SINK_V_NUM);
2614 wfd_sink_error("failed to allocate memory for videobin\n");
2615 return MM_ERROR_WFD_NO_FREE_SPACE;
2618 /* create videobin */
2619 videobin[WFD_SINK_V_BIN].id = WFD_SINK_V_BIN;
2620 videobin[WFD_SINK_V_BIN].gst = gst_bin_new("videobin");
2621 if (!videobin[WFD_SINK_V_BIN].gst) {
2622 wfd_sink_error("failed to create videobin\n");
2626 /* queue - drm - parse - dec - sink */
2628 MMWFDSINK_CREATE_ELEMENT(videobin, WFD_SINK_V_QUEUE, "queue", "video_queue", TRUE);
2629 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, videobin[WFD_SINK_V_QUEUE].gst, "sink");
2630 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, videobin[WFD_SINK_V_QUEUE].gst, "src");
2631 if (videobin[WFD_SINK_V_QUEUE].gst) {
2632 g_object_set(G_OBJECT(videobin[WFD_SINK_V_QUEUE].gst), "max-size-bytes", 0, NULL);
2633 g_object_set(G_OBJECT(videobin[WFD_SINK_V_QUEUE].gst), "max-size-buffers", 0, NULL);
2634 g_object_set(G_OBJECT(videobin[WFD_SINK_V_QUEUE].gst), "max-size-time", (guint64)3000000000ULL, NULL);
2635 g_signal_connect(videobin[WFD_SINK_V_QUEUE].gst, "overrun",
2636 G_CALLBACK(__mm_wfd_sink_queue_overrun), wfd_sink);
2640 MMWFDSINK_CREATE_ELEMENT(videobin, WFD_SINK_V_PARSE, wfd_sink->ini.name_of_video_parser, "video_parser", TRUE);
2641 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, videobin[WFD_SINK_V_PARSE].gst, "sink");
2642 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, videobin[WFD_SINK_V_PARSE].gst, "src");
2643 if (videobin[WFD_SINK_V_PARSE].gst)
2644 g_object_set(G_OBJECT(videobin[WFD_SINK_V_PARSE].gst), "wfd-mode", TRUE, NULL);
2647 MMWFDSINK_CREATE_ELEMENT(videobin, WFD_SINK_V_DEC, wfd_sink->ini.name_of_video_decoder, "video_dec", TRUE);
2648 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, videobin[WFD_SINK_V_DEC].gst, "sink");
2649 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, videobin[WFD_SINK_V_DEC].gst, "src");
2650 if (videobin[WFD_SINK_V_DEC].gst) {
2651 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_videodec(wfd_sink, videobin[WFD_SINK_V_DEC].gst)) {
2652 wfd_sink_error("failed to set video sink property....\n");
2658 MMWFDSINK_CREATE_ELEMENT(videobin, WFD_SINK_V_SINK, wfd_sink->ini.name_of_video_sink, "video_sink", TRUE);
2659 MMWFDSINK_PAD_PROBE(wfd_sink, NULL, videobin[WFD_SINK_V_SINK].gst, "sink");
2660 if (videobin[WFD_SINK_V_SINK].gst) {
2661 if (MM_ERROR_NONE != __mm_wfd_sink_prepare_videosink(wfd_sink, videobin[WFD_SINK_V_SINK].gst)) {
2662 wfd_sink_error("failed to set video sink property....\n");
2667 /* adding created elements to videobin */
2668 if (!__mm_wfd_sink_gst_element_add_bucket_to_bin(GST_BIN_CAST(videobin[WFD_SINK_V_BIN].gst), element_bucket, FALSE)) {
2669 wfd_sink_error("failed to add elements\n");
2673 /* linking elements in the bucket by added order. */
2674 if (__mm_wfd_sink_gst_element_link_bucket(element_bucket) == -1) {
2675 wfd_sink_error("failed to link elements\n");
2679 /* get first element's sinkpad for creating ghostpad */
2680 first_element = (MMWFDSinkGstElement *)g_list_nth_data(element_bucket, 0);
2681 if (!first_element) {
2682 wfd_sink_error("failed to get first element of videobin\n");
2686 pad = gst_element_get_static_pad(GST_ELEMENT(first_element->gst), "sink");
2688 wfd_sink_error("failed to get pad from first element(%s) of videobin\n", GST_ELEMENT_NAME(first_element->gst));
2692 ghostpad = gst_ghost_pad_new("sink", pad);
2694 wfd_sink_error("failed to create ghostpad\n");
2698 if (FALSE == gst_element_add_pad(videobin[WFD_SINK_V_BIN].gst, ghostpad)) {
2699 wfd_sink_error("failed to add ghostpad to videobin\n");
2703 gst_object_unref(GST_OBJECT(pad));
2705 g_list_free(element_bucket);
2707 wfd_sink->video_bin_is_linked = TRUE;
2710 wfd_sink->pipeline->videobin = videobin;
2712 if (wfd_sink->ini.video_sink_async) {
2714 bus = gst_element_get_bus(videobin[WFD_SINK_V_BIN].gst);
2716 gst_bus_set_sync_handler(bus, _mm_wfd_bus_sync_callback, wfd_sink, NULL);
2717 gst_object_unref(bus);
2720 wfd_sink_debug_fleave();
2722 return MM_ERROR_NONE;
2726 wfd_sink_error("failed to create videobin, releasing all\n");
2729 gst_object_unref(GST_OBJECT(pad));
2733 gst_object_unref(GST_OBJECT(ghostpad));
2736 g_list_free(element_bucket);
2738 __mm_wfd_sink_destroy_videobin(wfd_sink);
2740 MMWFDSINK_FREEIF(videobin);
2742 return MM_ERROR_WFD_INTERNAL;
2745 static int __mm_wfd_sink_destroy_pipeline(mm_wfd_sink_t *wfd_sink)
2747 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
2749 wfd_sink_debug_fenter();
2751 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
2753 if (wfd_sink->prev_audio_dec_src_pad)
2754 gst_object_unref(GST_OBJECT(wfd_sink->prev_audio_dec_src_pad));
2755 wfd_sink->prev_audio_dec_src_pad = NULL;
2757 if (wfd_sink->next_audio_dec_sink_pad)
2758 gst_object_unref(GST_OBJECT(wfd_sink->next_audio_dec_sink_pad));
2759 wfd_sink->next_audio_dec_sink_pad = NULL;
2761 /* cleanup gst stuffs */
2762 if (wfd_sink->pipeline) {
2763 MMWFDSinkGstElement *mainbin = wfd_sink->pipeline->mainbin;
2766 MMWFDSinkGstElement *audiobin = wfd_sink->pipeline->audiobin;
2767 MMWFDSinkGstElement *videobin = wfd_sink->pipeline->videobin;
2769 if (MM_ERROR_NONE != __mm_wfd_sink_destroy_videobin(wfd_sink)) {
2770 wfd_sink_error("failed to destroy videobin\n");
2771 return MM_ERROR_WFD_INTERNAL;
2774 if (MM_ERROR_NONE != __mm_wfd_sink_destroy_audiobin(wfd_sink)) {
2775 wfd_sink_error("failed to destroy audiobin\n");
2776 return MM_ERROR_WFD_INTERNAL;
2779 ret = gst_element_set_state(mainbin[WFD_SINK_M_PIPE].gst, GST_STATE_NULL);
2780 if (ret != GST_STATE_CHANGE_SUCCESS) {
2781 wfd_sink_error("failed to change state of mainbin to NULL\n");
2782 return MM_ERROR_WFD_INTERNAL;
2785 gst_object_unref(GST_OBJECT(mainbin[WFD_SINK_M_PIPE].gst));
2787 MMWFDSINK_FREEIF(audiobin);
2788 MMWFDSINK_FREEIF(videobin);
2789 MMWFDSINK_FREEIF(mainbin);
2792 MMWFDSINK_FREEIF(wfd_sink->pipeline);
2795 wfd_sink->added_av_pad_num = 0;
2796 wfd_sink->audio_bin_is_linked = FALSE;
2797 wfd_sink->video_bin_is_linked = FALSE;
2798 wfd_sink->need_to_reset_basetime = FALSE;
2800 wfd_sink_debug_fleave();
2802 return MM_ERROR_NONE;
2806 __mm_wfd_sink_dump_pipeline_state(mm_wfd_sink_t *wfd_sink)
2808 GstIterator *iter = NULL;
2809 gboolean done = FALSE;
2811 GstElement *item = NULL;
2812 GstElementFactory *factory = NULL;
2814 GstState state = GST_STATE_VOID_PENDING;
2815 GstState pending = GST_STATE_VOID_PENDING;
2816 GstClockTime time = 200 * GST_MSECOND;
2818 wfd_sink_debug_fenter();
2820 wfd_sink_return_if_fail(wfd_sink &&
2821 wfd_sink->pipeline &&
2822 wfd_sink->pipeline->mainbin &&
2823 wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst);
2825 iter = gst_bin_iterate_recurse(GST_BIN(wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst));
2829 switch (gst_iterator_next(iter, (gpointer)&item)) {
2830 case GST_ITERATOR_OK:
2831 gst_element_get_state(GST_ELEMENT(item), &state, &pending, time);
2833 factory = gst_element_get_factory(item) ;
2835 wfd_sink_error("%s:%s : From:%s To:%s refcount : %d\n",
2836 GST_STR_NULL(GST_OBJECT_NAME(factory)),
2837 GST_STR_NULL(GST_ELEMENT_NAME(item)),
2838 gst_element_state_get_name(state),
2839 gst_element_state_get_name(pending),
2840 GST_OBJECT_REFCOUNT_VALUE(item));
2842 gst_object_unref(item);
2844 case GST_ITERATOR_RESYNC:
2845 gst_iterator_resync(iter);
2847 case GST_ITERATOR_ERROR:
2850 case GST_ITERATOR_DONE:
2857 item = GST_ELEMENT_CAST(wfd_sink->pipeline->mainbin[WFD_SINK_M_PIPE].gst);
2859 gst_element_get_state(GST_ELEMENT(item), &state, &pending, time);
2861 factory = gst_element_get_factory(item) ;
2863 wfd_sink_error("%s:%s : From:%s To:%s refcount : %d\n",
2864 GST_OBJECT_NAME(factory),
2865 GST_ELEMENT_NAME(item),
2866 gst_element_state_get_name(state),
2867 gst_element_state_get_name(pending),
2868 GST_OBJECT_REFCOUNT_VALUE(item));
2872 gst_iterator_free(iter);
2874 wfd_sink_debug_fleave();
2880 __mm_wfds_sink_get_state_name(MMWFDSinkStateType state)
2883 case MM_WFD_SINK_STATE_NONE:
2885 case MM_WFD_SINK_STATE_NULL:
2887 case MM_WFD_SINK_STATE_PREPARED:
2889 case MM_WFD_SINK_STATE_CONNECTED:
2891 case MM_WFD_SINK_STATE_PLAYING:
2893 case MM_WFD_SINK_STATE_PAUSED:
2895 case MM_WFD_SINK_STATE_DISCONNECTED:
2896 return "DISCONNECTED";
2901 static void __mm_wfd_sink_prepare_video_resolution(gint resolution, guint *CEA_resolution,
2902 guint *VESA_resolution, guint *HH_resolution)
2904 if (resolution == MM_WFD_SINK_RESOLUTION_UNKNOWN) return;
2906 *CEA_resolution = 0;
2907 *VESA_resolution = 0;
2910 if (resolution & MM_WFD_SINK_RESOLUTION_1920x1080_P30)
2911 *CEA_resolution |= WFD_CEA_1920x1080P30;
2913 if (resolution & MM_WFD_SINK_RESOLUTION_1280x720_P30)
2914 *CEA_resolution |= WFD_CEA_1280x720P30;
2916 if (resolution & MM_WFD_SINK_RESOLUTION_960x540_P30)
2917 *HH_resolution |= WFD_HH_960x540P30;
2919 if (resolution & MM_WFD_SINK_RESOLUTION_864x480_P30)
2920 *HH_resolution |= WFD_HH_864x480P30;
2922 if (resolution & MM_WFD_SINK_RESOLUTION_720x480_P60)
2923 *CEA_resolution |= WFD_CEA_720x480P60;
2925 if (resolution & MM_WFD_SINK_RESOLUTION_640x480_P60)
2926 *CEA_resolution |= WFD_CEA_640x480P60;
2928 if (resolution & MM_WFD_SINK_RESOLUTION_640x360_P30)
2929 *HH_resolution |= WFD_HH_640x360P30;
2932 int _mm_wfd_sink_set_resolution(mm_wfd_sink_t *wfd_sink, MMWFDSinkResolution resolution)
2934 MMWFDSinkStateType cur_state = MM_WFD_SINK_STATE_NONE;
2936 wfd_sink_debug_fenter();
2938 wfd_sink_return_val_if_fail(wfd_sink, MM_ERROR_WFD_NOT_INITIALIZED);
2940 MMWFDSINK_PRINT_STATE(wfd_sink);
2941 cur_state = MMWFDSINK_CURRENT_STATE(wfd_sink);
2942 if (cur_state != MM_WFD_SINK_STATE_NULL) {
2943 wfd_sink_error("This function must be called when MM_WFD_SINK_STATE_NULL");
2944 return MM_ERROR_WFD_INVALID_STATE;
2947 wfd_sink->supportive_resolution = resolution;
2949 wfd_sink_debug_fleave();
2951 return MM_ERROR_NONE;