[0.6.124] rearrange some functions
[platform/core/multimedia/libmm-player.git] / src / mm_player_gst.c
1 /*
2  * libmm-player
3  *
4  * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact: JongHyuk Choi <jhchoi.choi@samsung.com>, YeJin Cho <cho.yejin@samsung.com>,
7  * Seungbae Shin <seungbae.shin@samsung.com>, YoungHwan An <younghwan_.an@samsung.com>
8  *
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
12  *
13  * http://www.apache.org/licenses/LICENSE-2.0
14  *
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.
20  *
21  */
22
23 /*===========================================================================================
24 |                                                                                                                                                                                       |
25 |  INCLUDE FILES                                                                                                                                                        |
26 |                                                                                                                                                                                       |
27 ========================================================================================== */
28 #include <dlog.h>
29 #include <mm_error.h>
30 #include <mm_attrs_private.h>
31
32 #include "mm_player_gst.h"
33 #include "mm_player_priv.h"
34 #include "mm_player_attrs.h"
35 #include "mm_player_utils.h"
36
37 /*===========================================================================================
38 |                                                                                                                                                                                       |
39 |  LOCAL DEFINITIONS AND DECLARATIONS FOR MODULE                                                                                        |
40 |                                                                                                                                                                                       |
41 ========================================================================================== */
42
43 /*---------------------------------------------------------------------------
44 |    GLOBAL CONSTANT DEFINITIONS:                                                                                       |
45 ---------------------------------------------------------------------------*/
46
47 /*---------------------------------------------------------------------------
48 |    IMPORTED VARIABLE DECLARATIONS:                                                                            |
49 ---------------------------------------------------------------------------*/
50
51 /*---------------------------------------------------------------------------
52 |    IMPORTED FUNCTION DECLARATIONS:                                                                            |
53 ---------------------------------------------------------------------------*/
54
55 /*---------------------------------------------------------------------------
56 |    LOCAL #defines:                                                                                                            |
57 ---------------------------------------------------------------------------*/
58
59 /*---------------------------------------------------------------------------
60 |    LOCAL CONSTANT DEFINITIONS:                                                                                        |
61 ---------------------------------------------------------------------------*/
62
63 /*---------------------------------------------------------------------------
64 |    LOCAL DATA TYPE DEFINITIONS:                                                                                       |
65 ---------------------------------------------------------------------------*/
66
67 /*---------------------------------------------------------------------------
68 |    GLOBAL VARIABLE DEFINITIONS:                                                                                       |
69 ---------------------------------------------------------------------------*/
70
71 /*---------------------------------------------------------------------------
72 |    LOCAL VARIABLE DEFINITIONS:                                                                                        |
73 ---------------------------------------------------------------------------*/
74
75 /*---------------------------------------------------------------------------
76 |    LOCAL FUNCTION PROTOTYPES:                                                                                         |
77 ---------------------------------------------------------------------------*/
78
79 /*===========================================================================================
80 |                                                                                                                                                                                       |
81 |  FUNCTION DEFINITIONS                                                                                                                                         |
82 |                                                                                                                                                                                       |
83 ========================================================================================== */
84
85 /* NOTE : decide gstreamer state whether there is some playable track or not. */
86 static gint
87 __mmplayer_gst_transform_gsterror(mm_player_t* player, GstMessage * message, GError* error)
88 {
89         gchar *src_element_name = NULL;
90         GstElement *src_element = NULL;
91         GstElementFactory *factory = NULL;
92         const gchar* klass = NULL;
93
94         MMPLAYER_FENTER();
95
96         MMPLAYER_RETURN_VAL_IF_FAIL(message, MM_ERROR_INVALID_ARGUMENT);
97         MMPLAYER_RETURN_VAL_IF_FAIL(message->src, MM_ERROR_INVALID_ARGUMENT);
98         MMPLAYER_RETURN_VAL_IF_FAIL(error, MM_ERROR_INVALID_ARGUMENT);
99         MMPLAYER_RETURN_VAL_IF_FAIL(player &&
100                                                                 player->pipeline &&
101                                                                 player->pipeline->mainbin, MM_ERROR_PLAYER_NOT_INITIALIZED);
102
103         src_element = GST_ELEMENT_CAST(message->src);
104         if (!src_element)
105                 goto INTERNAL_ERROR;
106
107         src_element_name = GST_ELEMENT_NAME(src_element);
108         if (!src_element_name)
109                 goto INTERNAL_ERROR;
110
111         factory = gst_element_get_factory(src_element);
112         if (!factory)
113                 goto INTERNAL_ERROR;
114
115         klass = gst_element_factory_get_metadata(factory, GST_ELEMENT_METADATA_KLASS);
116         if (!klass)
117                 goto INTERNAL_ERROR;
118
119         LOGD("error code=%d, msg=%s, src element=%s, class=%s\n",
120                         error->code, error->message, src_element_name, klass);
121
122         /* check whether the error is posted from not-activated track or not */
123         if (player->pipeline->mainbin[MMPLAYER_M_A_INPUT_SELECTOR].gst) {
124                 int msg_src_pos = 0;
125                 gint active_pad_index = player->selector[MM_PLAYER_TRACK_TYPE_AUDIO].active_pad_index;
126                 LOGD("current  active pad index  -%d", active_pad_index);
127
128                 if  (src_element_name) {
129                         int idx = 0;
130
131                         if (player->audio_decoders) {
132                                 GList *adec = player->audio_decoders;
133                                 for (; adec ; adec = g_list_next(adec)) {
134                                         gchar *name = adec->data;
135
136                                         LOGD("found audio decoder name  = %s", name);
137                                         if (g_strrstr(name, src_element_name)) {
138                                                 msg_src_pos = idx;
139                                                 break;
140                                         }
141                                         idx++;
142                                 }
143                         }
144                         LOGD("active pad = %d, error src index = %d", active_pad_index,  msg_src_pos);
145                 }
146
147                 if (active_pad_index != msg_src_pos) {
148                         LOGD("skip error because error is posted from no activated track");
149                         return MM_ERROR_NONE;
150                 }
151         }
152
153         switch (error->code) {
154         case GST_STREAM_ERROR_DECODE:
155         {
156                 /* Demuxer can't parse one track because it's corrupted.
157                  * So, the decoder for it is not linked.
158                  * But, it has one playable track.
159                  */
160                 if (g_strrstr(klass, "Demux")) {
161                         if (player->can_support_codec == FOUND_PLUGIN_VIDEO) {
162                                 return MM_ERROR_PLAYER_AUDIO_CODEC_NOT_FOUND;
163                         } else if (player->can_support_codec == FOUND_PLUGIN_AUDIO) {
164                                 return MM_ERROR_PLAYER_VIDEO_CODEC_NOT_FOUND;
165                         } else {
166                                 if (player->pipeline->audiobin) // PCM
167                                         return MM_ERROR_PLAYER_VIDEO_CODEC_NOT_FOUND;
168                                 else
169                                         goto CODEC_NOT_FOUND;
170                         }
171                 }
172                 return MM_ERROR_PLAYER_INVALID_STREAM;
173         }
174                 break;
175
176         case GST_STREAM_ERROR_CODEC_NOT_FOUND:
177         case GST_STREAM_ERROR_TYPE_NOT_FOUND:
178         case GST_STREAM_ERROR_WRONG_TYPE:
179         {
180                 if (src_element == player->pipeline->mainbin[MMPLAYER_M_SUBPARSE].gst) {
181                         LOGE("Not supported subtitle.");
182                         return MM_ERROR_PLAYER_NOT_SUPPORTED_SUBTITLE;
183                 }
184                 return MM_ERROR_PLAYER_NOT_SUPPORTED_FORMAT;
185         }
186
187         case GST_STREAM_ERROR_FAILED:
188         {
189                 /* Decoder Custom Message */
190                 if (strstr(error->message, "ongoing")) {
191                         if (strncasecmp(klass, "audio", 5)) {
192                                 if ((player->can_support_codec & FOUND_PLUGIN_VIDEO)) {
193                                         LOGD("Video can keep playing.\n");
194                                         return MM_ERROR_PLAYER_AUDIO_CODEC_NOT_FOUND;
195                                 } else
196                                         goto CODEC_NOT_FOUND;
197
198                         } else if (strncasecmp(klass, "video", 5)) {
199                                 if ((player->can_support_codec & FOUND_PLUGIN_AUDIO)) {
200                                         LOGD("Audio can keep playing.\n");
201                                         return MM_ERROR_PLAYER_VIDEO_CODEC_NOT_FOUND;
202                                 } else
203                                         goto CODEC_NOT_FOUND;
204                         }
205                 }
206                 return MM_ERROR_PLAYER_NOT_SUPPORTED_FORMAT;
207         }
208                 break;
209
210         case GST_STREAM_ERROR_DECRYPT:
211         case GST_STREAM_ERROR_DECRYPT_NOKEY:
212         {
213                 LOGE("decryption error, [%s] failed, reason : [%s]\n", src_element_name, error->message);
214
215                 if (strstr(error->message, "rights expired"))
216                         return MM_ERROR_PLAYER_DRM_EXPIRED;
217                 else if (strstr(error->message, "no rights"))
218                         return MM_ERROR_PLAYER_DRM_NO_LICENSE;
219                 else if (strstr(error->message, "has future rights"))
220                         return MM_ERROR_PLAYER_DRM_FUTURE_USE;
221                 else if (strstr(error->message, "opl violation"))
222                         return MM_ERROR_PLAYER_DRM_OUTPUT_PROTECTION;
223                 return MM_ERROR_PLAYER_DRM_NOT_AUTHORIZED;
224         }
225                 break;
226
227         default:
228                 break;
229         }
230
231         MMPLAYER_FLEAVE();
232
233         return MM_ERROR_PLAYER_INVALID_STREAM;
234
235 INTERNAL_ERROR:
236         return MM_ERROR_PLAYER_INTERNAL;
237
238 CODEC_NOT_FOUND:
239         LOGD("not found any available codec. Player should be destroyed.\n");
240         return MM_ERROR_PLAYER_CODEC_NOT_FOUND;
241 }
242
243 gint
244 __mmplayer_gst_handle_core_error(mm_player_t* player, int code)
245 {
246         gint trans_err = MM_ERROR_NONE;
247
248         MMPLAYER_FENTER();
249
250         MMPLAYER_RETURN_VAL_IF_FAIL(player, MM_ERROR_PLAYER_NOT_INITIALIZED);
251
252         switch (code) {
253         case GST_CORE_ERROR_MISSING_PLUGIN:
254                 return MM_ERROR_PLAYER_NOT_SUPPORTED_FORMAT;
255         case GST_CORE_ERROR_STATE_CHANGE:
256         case GST_CORE_ERROR_SEEK:
257         case GST_CORE_ERROR_NOT_IMPLEMENTED:
258         case GST_CORE_ERROR_FAILED:
259         case GST_CORE_ERROR_TOO_LAZY:
260         case GST_CORE_ERROR_PAD:
261         case GST_CORE_ERROR_THREAD:
262         case GST_CORE_ERROR_NEGOTIATION:
263         case GST_CORE_ERROR_EVENT:
264         case GST_CORE_ERROR_CAPS:
265         case GST_CORE_ERROR_TAG:
266         case GST_CORE_ERROR_CLOCK:
267         case GST_CORE_ERROR_DISABLED:
268         default:
269                 trans_err = MM_ERROR_PLAYER_INVALID_STREAM;
270                 break;
271         }
272
273         MMPLAYER_FLEAVE();
274
275         return trans_err;
276 }
277
278 gint
279 __mmplayer_gst_handle_library_error(mm_player_t* player, int code)
280 {
281         gint trans_err = MM_ERROR_NONE;
282
283         MMPLAYER_FENTER();
284
285         MMPLAYER_RETURN_VAL_IF_FAIL(player, MM_ERROR_PLAYER_NOT_INITIALIZED);
286
287         switch (code) {
288         case GST_LIBRARY_ERROR_FAILED:
289         case GST_LIBRARY_ERROR_TOO_LAZY:
290         case GST_LIBRARY_ERROR_INIT:
291         case GST_LIBRARY_ERROR_SHUTDOWN:
292         case GST_LIBRARY_ERROR_SETTINGS:
293         case GST_LIBRARY_ERROR_ENCODE:
294         default:
295                 trans_err =  MM_ERROR_PLAYER_INVALID_STREAM;
296                 break;
297         }
298
299         MMPLAYER_FLEAVE();
300
301         return trans_err;
302 }
303
304 gint
305 __mmplayer_gst_handle_resource_error(mm_player_t* player, int code, GstMessage * message)
306 {
307         gint trans_err = MM_ERROR_NONE;
308
309         MMPLAYER_FENTER();
310
311         MMPLAYER_RETURN_VAL_IF_FAIL(player, MM_ERROR_PLAYER_NOT_INITIALIZED);
312
313         switch (code) {
314         case GST_RESOURCE_ERROR_NO_SPACE_LEFT:
315                 trans_err = MM_ERROR_PLAYER_NO_FREE_SPACE;
316                 break;
317         case GST_RESOURCE_ERROR_NOT_FOUND:
318         case GST_RESOURCE_ERROR_OPEN_READ:
319                 if (MMPLAYER_IS_HTTP_STREAMING(player) || MMPLAYER_IS_HTTP_LIVE_STREAMING(player)
320                         || MMPLAYER_IS_RTSP_STREAMING(player)) {
321                         trans_err = MM_ERROR_PLAYER_STREAMING_CONNECTION_FAIL;
322                         break;
323                 }
324         case GST_RESOURCE_ERROR_READ:
325                 if (MMPLAYER_IS_HTTP_STREAMING(player) || MMPLAYER_IS_HTTP_LIVE_STREAMING(player)
326                         || MMPLAYER_IS_RTSP_STREAMING(player)) {
327                         trans_err = MM_ERROR_PLAYER_STREAMING_FAIL;
328                         break;
329                 } else if (message != NULL && message->src != NULL) {
330                         storage_state_e storage_state = STORAGE_STATE_UNMOUNTABLE;
331                         MMPlayerPathType path_type = MMPLAYER_PATH_MAX;
332
333                         if (message->src == (GstObject *)player->pipeline->mainbin[MMPLAYER_M_SRC].gst)
334                                 path_type = MMPLAYER_PATH_VOD;
335                         else if (message->src == (GstObject *)player->pipeline->mainbin[MMPLAYER_M_SUBSRC].gst)
336                                 path_type = MMPLAYER_PATH_TEXT;
337
338                         if (path_type != MMPLAYER_PATH_MAX && player->storage_info[path_type].type == STORAGE_TYPE_EXTERNAL) {
339                                 /* check storage state */
340                                 storage_get_state(player->storage_info[path_type].id, &storage_state);
341                                 player->storage_info[path_type].state = storage_state;
342                                 LOGW("path %d, storage state %d:%d", path_type, player->storage_info[path_type].id, storage_state);
343                         }
344                 } /* fall through */
345         case GST_RESOURCE_ERROR_WRITE:
346         case GST_RESOURCE_ERROR_FAILED:
347         case GST_RESOURCE_ERROR_SEEK:
348         case GST_RESOURCE_ERROR_TOO_LAZY:
349         case GST_RESOURCE_ERROR_BUSY:
350         case GST_RESOURCE_ERROR_OPEN_WRITE:
351         case GST_RESOURCE_ERROR_OPEN_READ_WRITE:
352         case GST_RESOURCE_ERROR_CLOSE:
353         case GST_RESOURCE_ERROR_SYNC:
354         case GST_RESOURCE_ERROR_SETTINGS:
355         default:
356                 trans_err = MM_ERROR_PLAYER_INTERNAL;
357         break;
358         }
359
360         MMPLAYER_FLEAVE();
361
362         return trans_err;
363 }
364
365 gint
366 __mmplayer_gst_handle_stream_error(mm_player_t* player, GError* error, GstMessage * message)
367 {
368         gint trans_err = MM_ERROR_NONE;
369
370         MMPLAYER_FENTER();
371
372         MMPLAYER_RETURN_VAL_IF_FAIL(player, MM_ERROR_PLAYER_NOT_INITIALIZED);
373         MMPLAYER_RETURN_VAL_IF_FAIL(error, MM_ERROR_INVALID_ARGUMENT);
374         MMPLAYER_RETURN_VAL_IF_FAIL(message, MM_ERROR_INVALID_ARGUMENT);
375
376         switch (error->code) {
377         case GST_STREAM_ERROR_FAILED:
378         case GST_STREAM_ERROR_TYPE_NOT_FOUND:
379         case GST_STREAM_ERROR_DECODE:
380         case GST_STREAM_ERROR_WRONG_TYPE:
381         case GST_STREAM_ERROR_DECRYPT:
382         case GST_STREAM_ERROR_DECRYPT_NOKEY:
383         case GST_STREAM_ERROR_CODEC_NOT_FOUND:
384                 trans_err = __mmplayer_gst_transform_gsterror(player, message, error);
385                 break;
386
387         case GST_STREAM_ERROR_NOT_IMPLEMENTED:
388         case GST_STREAM_ERROR_TOO_LAZY:
389         case GST_STREAM_ERROR_ENCODE:
390         case GST_STREAM_ERROR_DEMUX:
391         case GST_STREAM_ERROR_MUX:
392         case GST_STREAM_ERROR_FORMAT:
393         default:
394                 trans_err = MM_ERROR_PLAYER_INVALID_STREAM;
395                 break;
396         }
397
398         MMPLAYER_FLEAVE();
399
400         return trans_err;
401 }
402
403 gboolean
404 __mmplayer_handle_gst_error(mm_player_t* player, GstMessage * message, GError* error)
405 {
406         MMMessageParamType msg_param;
407         gchar *msg_src_element;
408
409         MMPLAYER_FENTER();
410
411         MMPLAYER_RETURN_VAL_IF_FAIL(player, FALSE);
412         MMPLAYER_RETURN_VAL_IF_FAIL(error, FALSE);
413
414         /* NOTE : do somthing necessary inside of __gst_handle_XXX_error. not here */
415
416         memset(&msg_param, 0, sizeof(MMMessageParamType));
417
418         if (error->domain == GST_CORE_ERROR) {
419                 msg_param.code = __mmplayer_gst_handle_core_error(player, error->code);
420         } else if (error->domain == GST_LIBRARY_ERROR) {
421                 msg_param.code = __mmplayer_gst_handle_library_error(player, error->code);
422         } else if (error->domain == GST_RESOURCE_ERROR) {
423                 msg_param.code = __mmplayer_gst_handle_resource_error(player, error->code, message);
424         } else if (error->domain == GST_STREAM_ERROR) {
425                 msg_param.code = __mmplayer_gst_handle_stream_error(player, error, message);
426         } else {
427                 LOGW("This error domain is not defined.\n");
428
429                 /* we treat system error as an internal error */
430                 msg_param.code = MM_ERROR_PLAYER_INVALID_STREAM;
431         }
432
433         if (message->src) {
434                 msg_src_element = GST_ELEMENT_NAME(GST_ELEMENT_CAST(message->src));
435
436                 msg_param.data = (void *) error->message;
437
438                 LOGE("-Msg src : [%s]   Domain : [%s]   Error : [%s]  Code : [%d] is tranlated to error code : [0x%x]\n",
439                         msg_src_element, g_quark_to_string(error->domain), error->message, error->code, msg_param.code);
440         }
441
442         /* no error */
443         if (msg_param.code == MM_ERROR_NONE)
444                 return TRUE;
445
446         /* skip error to avoid duplicated posting */
447         if (((player->storage_info[MMPLAYER_PATH_VOD].type == STORAGE_TYPE_EXTERNAL) &&
448                  (player->storage_info[MMPLAYER_PATH_VOD].state <= STORAGE_STATE_REMOVED)) ||
449                 ((player->storage_info[MMPLAYER_PATH_TEXT].type == STORAGE_TYPE_EXTERNAL) &&
450                  (player->storage_info[MMPLAYER_PATH_TEXT].state <= STORAGE_STATE_REMOVED))) {
451
452                 /* The error will be handled by mused.
453                  * @ref _mmplayer_manage_external_storage_state() */
454
455                 LOGW("storage is removed, skip error post");
456                 return TRUE;
457         }
458
459         /* post error to application */
460         if (!player->msg_posted) {
461                 MMPLAYER_POST_MSG(player, MM_MESSAGE_ERROR, &msg_param);
462                 /* don't post more if one was sent already */
463                 player->msg_posted = TRUE;
464         } else
465                 LOGD("skip error post because it's sent already.\n");
466
467         MMPLAYER_FLEAVE();
468
469         return TRUE;
470 }
471
472 static gboolean
473 __mmplayer_handle_streaming_error(mm_player_t* player, GstMessage * message)
474 {
475         LOGD("\n");
476         MMMessageParamType msg_param;
477         gchar *msg_src_element = NULL;
478         GstStructure *s = NULL;
479         guint error_id = 0;
480         gchar *error_string = NULL;
481
482         MMPLAYER_FENTER();
483
484         MMPLAYER_RETURN_VAL_IF_FAIL(player, FALSE);
485         MMPLAYER_RETURN_VAL_IF_FAIL(message, FALSE);
486
487         s = gst_structure_copy(gst_message_get_structure(message));
488
489
490         if (!gst_structure_get_uint(s, "error_id", &error_id))
491                 error_id = MMPLAYER_STREAMING_ERROR_NONE;
492
493         switch (error_id) {
494         case MMPLAYER_STREAMING_ERROR_UNSUPPORTED_AUDIO:
495                 msg_param.code = MM_ERROR_PLAYER_STREAMING_UNSUPPORTED_AUDIO;
496                 break;
497         case MMPLAYER_STREAMING_ERROR_UNSUPPORTED_VIDEO:
498                 msg_param.code = MM_ERROR_PLAYER_STREAMING_UNSUPPORTED_VIDEO;
499                 break;
500         case MMPLAYER_STREAMING_ERROR_CONNECTION_FAIL:
501                 msg_param.code = MM_ERROR_PLAYER_STREAMING_CONNECTION_FAIL;
502                 break;
503         case MMPLAYER_STREAMING_ERROR_DNS_FAIL:
504                 msg_param.code = MM_ERROR_PLAYER_STREAMING_DNS_FAIL;
505                 break;
506         case MMPLAYER_STREAMING_ERROR_SERVER_DISCONNECTED:
507                 msg_param.code = MM_ERROR_PLAYER_STREAMING_SERVER_DISCONNECTED;
508                 break;
509         case MMPLAYER_STREAMING_ERROR_BAD_SERVER:
510                 msg_param.code = MM_ERROR_PLAYER_STREAMING_BAD_SERVER;
511                 break;
512         case MMPLAYER_STREAMING_ERROR_INVALID_PROTOCOL:
513                 msg_param.code = MM_ERROR_PLAYER_STREAMING_INVALID_PROTOCOL;
514                 break;
515         case MMPLAYER_STREAMING_ERROR_INVALID_URL:
516                 msg_param.code = MM_ERROR_PLAYER_STREAMING_INVALID_URL;
517                 break;
518         case MMPLAYER_STREAMING_ERROR_UNEXPECTED_MSG:
519                 msg_param.code = MM_ERROR_PLAYER_STREAMING_UNEXPECTED_MSG;
520                 break;
521         case MMPLAYER_STREAMING_ERROR_OUT_OF_MEMORIES:
522                 msg_param.code = MM_ERROR_PLAYER_STREAMING_OUT_OF_MEMORIES;
523                 break;
524         case MMPLAYER_STREAMING_ERROR_RTSP_TIMEOUT:
525                 msg_param.code = MM_ERROR_PLAYER_STREAMING_RTSP_TIMEOUT;
526                 break;
527         case MMPLAYER_STREAMING_ERROR_BAD_REQUEST:
528                 msg_param.code = MM_ERROR_PLAYER_STREAMING_BAD_REQUEST;
529                 break;
530         case MMPLAYER_STREAMING_ERROR_NOT_AUTHORIZED:
531                 msg_param.code = MM_ERROR_PLAYER_STREAMING_NOT_AUTHORIZED;
532                 break;
533         case MMPLAYER_STREAMING_ERROR_PAYMENT_REQUIRED:
534                 msg_param.code = MM_ERROR_PLAYER_STREAMING_PAYMENT_REQUIRED;
535                 break;
536         case MMPLAYER_STREAMING_ERROR_FORBIDDEN:
537                 msg_param.code = MM_ERROR_PLAYER_STREAMING_FORBIDDEN;
538                 break;
539         case MMPLAYER_STREAMING_ERROR_CONTENT_NOT_FOUND:
540                 msg_param.code = MM_ERROR_PLAYER_STREAMING_CONTENT_NOT_FOUND;
541                 break;
542         case MMPLAYER_STREAMING_ERROR_METHOD_NOT_ALLOWED:
543                 msg_param.code = MM_ERROR_PLAYER_STREAMING_METHOD_NOT_ALLOWED;
544                 break;
545         case MMPLAYER_STREAMING_ERROR_NOT_ACCEPTABLE:
546                 msg_param.code = MM_ERROR_PLAYER_STREAMING_NOT_ACCEPTABLE;
547                 break;
548         case MMPLAYER_STREAMING_ERROR_PROXY_AUTHENTICATION_REQUIRED:
549                 msg_param.code = MM_ERROR_PLAYER_STREAMING_PROXY_AUTHENTICATION_REQUIRED;
550                 break;
551         case MMPLAYER_STREAMING_ERROR_SERVER_TIMEOUT:
552                 msg_param.code = MM_ERROR_PLAYER_STREAMING_SERVER_TIMEOUT;
553                 break;
554         case MMPLAYER_STREAMING_ERROR_GONE:
555                 msg_param.code = MM_ERROR_PLAYER_STREAMING_GONE;
556                 break;
557         case MMPLAYER_STREAMING_ERROR_LENGTH_REQUIRED:
558                 msg_param.code = MM_ERROR_PLAYER_STREAMING_LENGTH_REQUIRED;
559                 break;
560         case MMPLAYER_STREAMING_ERROR_PRECONDITION_FAILED:
561                 msg_param.code = MM_ERROR_PLAYER_STREAMING_PRECONDITION_FAILED;
562                 break;
563         case MMPLAYER_STREAMING_ERROR_REQUEST_ENTITY_TOO_LARGE:
564                 msg_param.code = MM_ERROR_PLAYER_STREAMING_REQUEST_ENTITY_TOO_LARGE;
565                 break;
566         case MMPLAYER_STREAMING_ERROR_REQUEST_URI_TOO_LARGE:
567                 msg_param.code = MM_ERROR_PLAYER_STREAMING_REQUEST_URI_TOO_LARGE;
568                 break;
569         case MMPLAYER_STREAMING_ERROR_UNSUPPORTED_MEDIA_TYPE:
570                 msg_param.code = MM_ERROR_PLAYER_STREAMING_UNSUPPORTED_MEDIA_TYPE;
571                 break;
572         case MMPLAYER_STREAMING_ERROR_PARAMETER_NOT_UNDERSTOOD:
573                 msg_param.code = MM_ERROR_PLAYER_STREAMING_PARAMETER_NOT_UNDERSTOOD;
574                 break;
575         case MMPLAYER_STREAMING_ERROR_CONFERENCE_NOT_FOUND:
576                 msg_param.code = MM_ERROR_PLAYER_STREAMING_CONFERENCE_NOT_FOUND;
577                 break;
578         case MMPLAYER_STREAMING_ERROR_NOT_ENOUGH_BANDWIDTH:
579                 msg_param.code = MM_ERROR_PLAYER_STREAMING_NOT_ENOUGH_BANDWIDTH;
580                 break;
581         case MMPLAYER_STREAMING_ERROR_NO_SESSION_ID:
582                 msg_param.code = MM_ERROR_PLAYER_STREAMING_NO_SESSION_ID;
583                 break;
584         case MMPLAYER_STREAMING_ERROR_METHOD_NOT_VALID_IN_THIS_STATE:
585                 msg_param.code = MM_ERROR_PLAYER_STREAMING_METHOD_NOT_VALID_IN_THIS_STATE;
586                 break;
587         case MMPLAYER_STREAMING_ERROR_HEADER_FIELD_NOT_VALID_FOR_SOURCE:
588                 msg_param.code = MM_ERROR_PLAYER_STREAMING_HEADER_FIELD_NOT_VALID_FOR_SOURCE;
589                 break;
590         case MMPLAYER_STREAMING_ERROR_INVALID_RANGE:
591                 msg_param.code = MM_ERROR_PLAYER_STREAMING_INVALID_RANGE;
592                 break;
593         case MMPLAYER_STREAMING_ERROR_PARAMETER_IS_READONLY:
594                 msg_param.code = MM_ERROR_PLAYER_STREAMING_PARAMETER_IS_READONLY;
595                 break;
596         case MMPLAYER_STREAMING_ERROR_AGGREGATE_OP_NOT_ALLOWED:
597                 msg_param.code = MM_ERROR_PLAYER_STREAMING_AGGREGATE_OP_NOT_ALLOWED;
598                 break;
599         case MMPLAYER_STREAMING_ERROR_ONLY_AGGREGATE_OP_ALLOWED:
600                 msg_param.code = MM_ERROR_PLAYER_STREAMING_ONLY_AGGREGATE_OP_ALLOWED;
601                 break;
602         case MMPLAYER_STREAMING_ERROR_BAD_TRANSPORT:
603                 msg_param.code = MM_ERROR_PLAYER_STREAMING_BAD_TRANSPORT;
604                 break;
605         case MMPLAYER_STREAMING_ERROR_DESTINATION_UNREACHABLE:
606                 msg_param.code = MM_ERROR_PLAYER_STREAMING_DESTINATION_UNREACHABLE;
607                 break;
608         case MMPLAYER_STREAMING_ERROR_INTERNAL_SERVER_ERROR:
609                 msg_param.code = MM_ERROR_PLAYER_STREAMING_INTERNAL_SERVER_ERROR;
610                 break;
611         case MMPLAYER_STREAMING_ERROR_NOT_IMPLEMENTED:
612                 msg_param.code = MM_ERROR_PLAYER_STREAMING_NOT_IMPLEMENTED;
613                 break;
614         case MMPLAYER_STREAMING_ERROR_BAD_GATEWAY:
615                 msg_param.code = MM_ERROR_PLAYER_STREAMING_BAD_GATEWAY;
616                 break;
617         case MMPLAYER_STREAMING_ERROR_SERVICE_UNAVAILABLE:
618                 msg_param.code = MM_ERROR_PLAYER_STREAMING_SERVICE_UNAVAILABLE;
619                 break;
620         case MMPLAYER_STREAMING_ERROR_GATEWAY_TIME_OUT:
621                 msg_param.code = MM_ERROR_PLAYER_STREAMING_GATEWAY_TIME_OUT;
622                 break;
623         case MMPLAYER_STREAMING_ERROR_RTSP_VERSION_NOT_SUPPORTED:
624                 msg_param.code = MM_ERROR_PLAYER_STREAMING_RTSP_VERSION_NOT_SUPPORTED;
625                 break;
626         case MMPLAYER_STREAMING_ERROR_OPTION_NOT_SUPPORTED:
627                 msg_param.code = MM_ERROR_PLAYER_STREAMING_OPTION_NOT_SUPPORTED;
628                 break;
629         default:
630                 {
631                         gst_structure_free(s);
632                         return MM_ERROR_PLAYER_STREAMING_FAIL;
633                 }
634         }
635
636         error_string = g_strdup(gst_structure_get_string(s, "error_string"));
637         if (error_string)
638                 msg_param.data = (void *) error_string;
639
640         if (message->src) {
641                 msg_src_element = GST_ELEMENT_NAME(GST_ELEMENT_CAST(message->src));
642
643                 LOGE("-Msg src : [%s] Code : [%x] Error : [%s]  \n",
644                         msg_src_element, msg_param.code, (char*)msg_param.data);
645         }
646
647         /* post error to application */
648         if (!player->msg_posted) {
649                 MMPLAYER_POST_MSG(player, MM_MESSAGE_ERROR, &msg_param);
650
651                 /* don't post more if one was sent already */
652                 player->msg_posted = TRUE;
653         } else
654                 LOGD("skip error post because it's sent already.\n");
655
656         gst_structure_free(s);
657         MMPLAYER_FLEAVE();
658         g_free(error_string);
659
660         return TRUE;
661
662 }
663
664 static void
665 __mmplayer_get_metadata_360_from_tags(GstTagList *tags, mm_player_spherical_metadata_t *metadata)
666 {
667         gst_tag_list_get_int(tags, "is_spherical", &metadata->is_spherical);
668         gst_tag_list_get_int(tags, "is_stitched", &metadata->is_stitched);
669         gst_tag_list_get_string(tags, "stitching_software",
670                         &metadata->stitching_software);
671         gst_tag_list_get_string(tags, "projection_type",
672                         &metadata->projection_type_string);
673         gst_tag_list_get_string(tags, "stereo_mode", &metadata->stereo_mode_string);
674         gst_tag_list_get_int(tags, "source_count", &metadata->source_count);
675         gst_tag_list_get_int(tags, "init_view_heading",
676                         &metadata->init_view_heading);
677         gst_tag_list_get_int(tags, "init_view_pitch", &metadata->init_view_pitch);
678         gst_tag_list_get_int(tags, "init_view_roll", &metadata->init_view_roll);
679         gst_tag_list_get_int(tags, "timestamp", &metadata->timestamp);
680         gst_tag_list_get_int(tags, "full_pano_width_pixels",
681                         &metadata->full_pano_width_pixels);
682         gst_tag_list_get_int(tags, "full_pano_height_pixels",
683                         &metadata->full_pano_height_pixels);
684         gst_tag_list_get_int(tags, "cropped_area_image_width",
685                         &metadata->cropped_area_image_width);
686         gst_tag_list_get_int(tags, "cropped_area_image_height",
687                         &metadata->cropped_area_image_height);
688         gst_tag_list_get_int(tags, "cropped_area_left",
689                         &metadata->cropped_area_left);
690         gst_tag_list_get_int(tags, "cropped_area_top", &metadata->cropped_area_top);
691         gst_tag_list_get_int(tags, "ambisonic_type", &metadata->ambisonic_type);
692         gst_tag_list_get_int(tags, "ambisonic_format", &metadata->ambisonic_format);
693         gst_tag_list_get_int(tags, "ambisonic_order", &metadata->ambisonic_order);
694 }
695
696 static gboolean
697 __mmplayer_gst_extract_tag_from_msg(mm_player_t* player, GstMessage* msg)
698 {
699
700 /* macro for better code readability */
701 #define MMPLAYER_UPDATE_TAG_STRING(gsttag, attribute, playertag) \
702 if (gst_tag_list_get_string(tag_list, gsttag, &string)) {\
703         if (string != NULL) { \
704                 SECURE_LOGD("update tag string : %s\n", string); \
705                 if (strlen(string) > MM_MAX_STRING_LENGTH) { \
706                         char *new_string = malloc(MM_MAX_STRING_LENGTH); \
707                         strncpy(new_string, string, MM_MAX_STRING_LENGTH-1); \
708                         new_string[MM_MAX_STRING_LENGTH-1] = '\0'; \
709                         mm_attrs_set_string_by_name(attribute, playertag, new_string); \
710                         g_free(new_string); \
711                         new_string = NULL; \
712                 } else { \
713                         mm_attrs_set_string_by_name(attribute, playertag, string); \
714                 } \
715                 g_free(string); \
716                 string = NULL; \
717         } \
718 }
719
720 #define MMPLAYER_UPDATE_TAG_IMAGE(gsttag, attribute, playertag) \
721 do {    \
722         GstSample *sample = NULL;\
723         if (gst_tag_list_get_sample_index(tag_list, gsttag, index, &sample)) {\
724                 GstMapInfo info = GST_MAP_INFO_INIT;\
725                 buffer = gst_sample_get_buffer(sample);\
726                 if (!gst_buffer_map(buffer, &info, GST_MAP_READ)) {\
727                         LOGD("failed to get image data from tag");\
728                         gst_sample_unref(sample);\
729                         return FALSE;\
730                 } \
731                 SECURE_LOGD("update album cover data : %p, size : %d\n", info.data, info.size);\
732                 MMPLAYER_FREEIF(player->album_art);\
733                 player->album_art = (gchar *)g_malloc(info.size);\
734                 if (player->album_art) {\
735                         memcpy(player->album_art, info.data, info.size);\
736                         mm_attrs_set_data_by_name(attribute, playertag, (void *)player->album_art, info.size);\
737                         if (MMPLAYER_IS_HTTP_LIVE_STREAMING(player)) {\
738                                 msg_param.data = (void *)player->album_art;\
739                                 msg_param.size = info.size;\
740                                 MMPLAYER_POST_MSG(player, MM_MESSAGE_IMAGE_BUFFER, &msg_param);\
741                                 SECURE_LOGD("post message image buffer data : %p, size : %d\n", info.data, info.size);\
742                         } \
743                 } \
744                 gst_buffer_unmap(buffer, &info);\
745                 gst_sample_unref(sample);\
746         }       \
747 } while (0)
748
749 #define MMPLAYER_UPDATE_TAG_UINT(gsttag, attribute, playertag) \
750 do {    \
751         if (gst_tag_list_get_uint(tag_list, gsttag, &v_uint)) { \
752                 if (v_uint) { \
753                         int i = 0; \
754                         gchar *tag_list_str = NULL; \
755                         MMPlayerTrackType track_type = MM_PLAYER_TRACK_TYPE_AUDIO; \
756                         if (strstr(GST_OBJECT_NAME(msg->src), "audio")) \
757                                 track_type = MM_PLAYER_TRACK_TYPE_AUDIO; \
758                         else if (strstr(GST_OBJECT_NAME(msg->src), "video")) \
759                                 track_type = MM_PLAYER_TRACK_TYPE_VIDEO; \
760                         else \
761                                 track_type = MM_PLAYER_TRACK_TYPE_TEXT; \
762                         if (!strncmp(gsttag, GST_TAG_BITRATE, strlen(GST_TAG_BITRATE))) { \
763                                 if (track_type == MM_PLAYER_TRACK_TYPE_AUDIO) \
764                                         mm_attrs_set_int_by_name(attribute, "content_audio_bitrate", v_uint); \
765                                 player->bitrate[track_type] = v_uint; \
766                                 player->total_bitrate = 0; \
767                                 for (i = 0; i < MM_PLAYER_STREAM_COUNT_MAX; i++) \
768                                         player->total_bitrate += player->bitrate[i]; \
769                                 mm_attrs_set_int_by_name(attribute, playertag, player->total_bitrate); \
770                                 SECURE_LOGD("update bitrate %d[bps] of stream #%d.\n", v_uint, (int)track_type); \
771                         } else if (!strncmp(gsttag, GST_TAG_MAXIMUM_BITRATE, strlen(GST_TAG_MAXIMUM_BITRATE))) { \
772                                 player->maximum_bitrate[track_type] = v_uint; \
773                                 player->total_maximum_bitrate = 0; \
774                                 for (i = 0; i < MM_PLAYER_STREAM_COUNT_MAX; i++) \
775                                         player->total_maximum_bitrate += player->maximum_bitrate[i]; \
776                                 mm_attrs_set_int_by_name(attribute, playertag, player->total_maximum_bitrate);\
777                                 SECURE_LOGD("update maximum bitrate %d[bps] of stream #%d\n", v_uint, (int)track_type);\
778                         } else { \
779                                 mm_attrs_set_int_by_name(attribute, playertag, v_uint); \
780                         } \
781                         v_uint = 0;\
782                         g_free(tag_list_str); \
783                 } \
784         } \
785 } while (0)
786
787 #define MMPLAYER_UPDATE_TAG_DATE(gsttag, attribute, playertag) \
788 if (gst_tag_list_get_date(tag_list, gsttag, &date)) {\
789         if (date != NULL) {\
790                 string = g_strdup_printf("%d", g_date_get_year(date));\
791                 mm_attrs_set_string_by_name(attribute, playertag, string);\
792                 SECURE_LOGD("metainfo year : %s\n", string);\
793                 MMPLAYER_FREEIF(string);\
794                 g_date_free(date);\
795         } \
796 }
797
798 #define MMPLAYER_UPDATE_TAG_DATE_TIME(gsttag, attribute, playertag) \
799 if (gst_tag_list_get_date_time(tag_list, gsttag, &datetime)) {\
800         if (datetime != NULL) {\
801                 string = g_strdup_printf("%d", gst_date_time_get_year(datetime));\
802                 mm_attrs_set_string_by_name(attribute, playertag, string);\
803                 SECURE_LOGD("metainfo year : %s\n", string);\
804                 MMPLAYER_FREEIF(string);\
805                 gst_date_time_unref(datetime);\
806         } \
807 }
808
809 #define MMPLAYER_UPDATE_TAG_UINT64(gsttag, attribute, playertag) \
810 if (gst_tag_list_get_uint64(tag_list, gsttag, &v_uint64)) {\
811         if (v_uint64) {\
812                 /* FIXIT : don't know how to store date */\
813                 g_assert(1);\
814                 v_uint64 = 0;\
815         } \
816 }
817
818 #define MMPLAYER_UPDATE_TAG_DOUBLE(gsttag, attribute, playertag) \
819 if (gst_tag_list_get_double(tag_list, gsttag, &v_double)) {\
820         if (v_double) {\
821                 /* FIXIT : don't know how to store date */\
822                 g_assert(1);\
823                 v_double = 0;\
824         } \
825 }
826
827         /* function start */
828         GstTagList* tag_list = NULL;
829
830         MMHandleType attrs = 0;
831
832         char *string = NULL;
833         guint v_uint = 0;
834         GDate *date = NULL;
835         GstDateTime *datetime = NULL;
836         /* album cover */
837         GstBuffer *buffer = NULL;
838         gint index = 0;
839         MMMessageParamType msg_param = {0, };
840
841         /* currently not used. but those are needed for above macro */
842         //guint64 v_uint64 = 0;
843         //gdouble v_double = 0;
844
845         MMPLAYER_RETURN_VAL_IF_FAIL(player && msg, FALSE);
846
847         attrs = MMPLAYER_GET_ATTRS(player);
848
849         MMPLAYER_RETURN_VAL_IF_FAIL(attrs, FALSE);
850
851         /* get tag list from gst message */
852         gst_message_parse_tag(msg, &tag_list);
853
854         /* store tags to player attributes */
855         MMPLAYER_UPDATE_TAG_STRING(GST_TAG_TITLE, attrs, "tag_title");
856         /* MMPLAYER_UPDATE_TAG_STRING(GST_TAG_TITLE_SORTNAME, ?, ?); */
857         MMPLAYER_UPDATE_TAG_STRING(GST_TAG_ARTIST, attrs, "tag_artist");
858         /* MMPLAYER_UPDATE_TAG_STRING(GST_TAG_ARTIST_SORTNAME, ?, ?); */
859         MMPLAYER_UPDATE_TAG_STRING(GST_TAG_ALBUM, attrs, "tag_album");
860         /* MMPLAYER_UPDATE_TAG_STRING(GST_TAG_ALBUM_SORTNAME, ?, ?); */
861         MMPLAYER_UPDATE_TAG_STRING(GST_TAG_COMPOSER, attrs, "tag_author");
862         MMPLAYER_UPDATE_TAG_DATE(GST_TAG_DATE, attrs, "tag_date");
863         MMPLAYER_UPDATE_TAG_DATE_TIME(GST_TAG_DATE_TIME, attrs, "tag_date");
864         MMPLAYER_UPDATE_TAG_STRING(GST_TAG_GENRE, attrs, "tag_genre");
865         /* MMPLAYER_UPDATE_TAG_STRING(GST_TAG_COMMENT, ?, ?); */
866         /* MMPLAYER_UPDATE_TAG_STRING(GST_TAG_EXTENDED_COMMENT, ?, ?); */
867         MMPLAYER_UPDATE_TAG_UINT(GST_TAG_TRACK_NUMBER, attrs, "tag_track_num");
868         /* MMPLAYER_UPDATE_TAG_UINT(GST_TAG_TRACK_COUNT, ?, ?); */
869         /* MMPLAYER_UPDATE_TAG_UINT(GST_TAG_ALBUM_VOLUME_NUMBER, ?, ?); */
870         /* MMPLAYER_UPDATE_TAG_UINT(GST_TAG_ALBUM_VOLUME_COUNT, ?, ?); */
871         /* MMPLAYER_UPDATE_TAG_STRING(GST_TAG_LOCATION, ?, ?); */
872         MMPLAYER_UPDATE_TAG_STRING(GST_TAG_DESCRIPTION, attrs, "tag_description");
873         /* MMPLAYER_UPDATE_TAG_STRING(GST_TAG_VERSION, ?, ?); */
874         /* MMPLAYER_UPDATE_TAG_STRING(GST_TAG_ISRC, ?, ?); */
875         /* MMPLAYER_UPDATE_TAG_STRING(GST_TAG_ORGANIZATION, ?, ?); */
876         MMPLAYER_UPDATE_TAG_STRING(GST_TAG_COPYRIGHT, attrs, "tag_copyright");
877         /* MMPLAYER_UPDATE_TAG_STRING(GST_TAG_COPYRIGHT_URI, ?, ?); */
878         /* MMPLAYER_UPDATE_TAG_STRING(GST_TAG_CONTACT, ?, ?); */
879         /* MMPLAYER_UPDATE_TAG_STRING(GST_TAG_LICENSE, ?, ?); */
880         /* MMPLAYER_UPDATE_TAG_STRING(GST_TAG_LICENSE_URI, ?, ?); */
881         /* MMPLAYER_UPDATE_TAG_STRING(GST_TAG_PERFORMER, ?, ?); */
882         /* MMPLAYER_UPDATE_TAG_UINT64(GST_TAG_DURATION, ?, ?); */
883         /* MMPLAYER_UPDATE_TAG_STRING(GST_TAG_CODEC, ?, ?); */
884         MMPLAYER_UPDATE_TAG_STRING(GST_TAG_VIDEO_CODEC, attrs, "content_video_codec");
885         MMPLAYER_UPDATE_TAG_STRING(GST_TAG_AUDIO_CODEC, attrs, "content_audio_codec");
886         MMPLAYER_UPDATE_TAG_UINT(GST_TAG_BITRATE, attrs, "content_bitrate");
887         MMPLAYER_UPDATE_TAG_UINT(GST_TAG_MAXIMUM_BITRATE, attrs, "content_max_bitrate");
888         MMPLAYER_UPDATE_TAG_LOCK(player);
889         MMPLAYER_UPDATE_TAG_IMAGE(GST_TAG_IMAGE, attrs, "tag_album_cover");
890         MMPLAYER_UPDATE_TAG_UNLOCK(player);
891         /* MMPLAYER_UPDATE_TAG_UINT(GST_TAG_NOMINAL_BITRATE, ?, ?); */
892         /* MMPLAYER_UPDATE_TAG_UINT(GST_TAG_MINIMUM_BITRATE, ?, ?); */
893         /* MMPLAYER_UPDATE_TAG_UINT(GST_TAG_SERIAL, ?, ?); */
894         /* MMPLAYER_UPDATE_TAG_STRING(GST_TAG_ENCODER, ?, ?); */
895         /* MMPLAYER_UPDATE_TAG_UINT(GST_TAG_ENCODER_VERSION, ?, ?); */
896         /* MMPLAYER_UPDATE_TAG_DOUBLE(GST_TAG_TRACK_GAIN, ?, ?); */
897         /* MMPLAYER_UPDATE_TAG_DOUBLE(GST_TAG_TRACK_PEAK, ?, ?); */
898         /* MMPLAYER_UPDATE_TAG_DOUBLE(GST_TAG_ALBUM_GAIN, ?, ?); */
899         /* MMPLAYER_UPDATE_TAG_DOUBLE(GST_TAG_ALBUM_PEAK, ?, ?); */
900         /* MMPLAYER_UPDATE_TAG_DOUBLE(GST_TAG_REFERENCE_LEVEL, ?, ?); */
901         /* MMPLAYER_UPDATE_TAG_STRING(GST_TAG_LANGUAGE_CODE, ?, ?); */
902         /* MMPLAYER_UPDATE_TAG_DOUBLE(GST_TAG_BEATS_PER_MINUTE, ?, ?); */
903         MMPLAYER_UPDATE_TAG_STRING(GST_TAG_IMAGE_ORIENTATION, attrs, "content_video_orientation");
904
905         if (strstr(GST_OBJECT_NAME(msg->src), "demux")) {
906                 if (player->video360_metadata.is_spherical == -1) {
907                         __mmplayer_get_metadata_360_from_tags(tag_list, &player->video360_metadata);
908                         mm_attrs_set_int_by_name(attrs, "content_video_is_spherical",
909                                         player->video360_metadata.is_spherical);
910                         if (player->video360_metadata.is_spherical == 1) {
911                                 LOGD("This is spherical content for 360 playback.");
912                                 player->is_content_spherical = TRUE;
913                         } else {
914                                 LOGD("This is not spherical content");
915                                 player->is_content_spherical = FALSE;
916                         }
917
918                         if (player->video360_metadata.projection_type_string) {
919                                 if (!strcmp(player->video360_metadata.projection_type_string, "equirectangular")) {
920                                         player->video360_metadata.projection_type = VIDEO360_PROJECTION_TYPE_EQUIRECTANGULAR;
921                                 } else {
922                                         LOGE("Projection %s: code not implemented.\n", player->video360_metadata.projection_type_string);
923                                         player->is_content_spherical = player->is_video360_enabled = FALSE;
924                                 }
925                         }
926
927                         if (player->video360_metadata.stereo_mode_string) {
928                                 if (!strcmp(player->video360_metadata.stereo_mode_string, "mono")) {
929                                         player->video360_metadata.stereo_mode = VIDEO360_MODE_MONOSCOPIC;
930                                 } else if (!strcmp(player->video360_metadata.stereo_mode_string, "left-right")) {
931                                         player->video360_metadata.stereo_mode = VIDEO360_MODE_STEREOSCOPIC_LEFT_RIGHT;
932                                 } else if (!strcmp(player->video360_metadata.stereo_mode_string, "top-bottom")) {
933                                         player->video360_metadata.stereo_mode = VIDEO360_MODE_STEREOSCOPIC_TOP_BOTTOM;
934                                 } else {
935                                         LOGE("Stereo mode %s: code not implemented.\n", player->video360_metadata.stereo_mode_string);
936                                         player->is_content_spherical = player->is_video360_enabled = FALSE;
937                                 }
938                         }
939                 }
940         }
941
942         if (mmf_attrs_commit(attrs))
943                 LOGE("failed to commit.\n");
944
945         gst_tag_list_free(tag_list);
946
947         return TRUE;
948 }
949
950 /* if retval is FALSE, it will be dropped for perfomance. */
951 static gboolean
952 __mmplayer_gst_check_useful_message(mm_player_t *player, GstMessage * message)
953 {
954         gboolean retval = FALSE;
955
956         if (!(player->pipeline && player->pipeline->mainbin)) {
957                 LOGE("player pipeline handle is null");
958                 return TRUE;
959         }
960
961         switch (GST_MESSAGE_TYPE(message)) {
962         case GST_MESSAGE_TAG:
963         case GST_MESSAGE_EOS:
964         case GST_MESSAGE_ERROR:
965         case GST_MESSAGE_WARNING:
966         case GST_MESSAGE_CLOCK_LOST:
967         case GST_MESSAGE_NEW_CLOCK:
968         case GST_MESSAGE_ELEMENT:
969         case GST_MESSAGE_DURATION_CHANGED:
970         case GST_MESSAGE_ASYNC_START:
971                 retval = TRUE;
972                 break;
973         case GST_MESSAGE_ASYNC_DONE:
974         case GST_MESSAGE_STATE_CHANGED:
975                 /* we only handle messages from pipeline */
976                 if ((message->src == (GstObject *)player->pipeline->mainbin[MMPLAYER_M_PIPE].gst) && (!player->gapless.reconfigure))
977                         retval = TRUE;
978                 else
979                         retval = FALSE;
980                 break;
981         case GST_MESSAGE_BUFFERING:
982         {
983                 gint buffer_percent = 0;
984
985                 retval = TRUE;
986                 gst_message_parse_buffering(message, &buffer_percent);
987                 if (buffer_percent != MAX_BUFFER_PERCENT) {
988                         LOGD("[%s] buffering msg %d%%!!\n", GST_OBJECT_NAME(GST_MESSAGE_SRC(message)), buffer_percent);
989                         break;
990                 }
991
992                 if (!MMPLAYER_CMD_TRYLOCK(player)) {
993                         LOGW("can't get cmd lock, send msg to bus");
994                         break;
995                 }
996
997                 if ((player->streamer) && (player->streamer->buffering_state & MM_PLAYER_BUFFERING_IN_PROGRESS)) {
998                         LOGD("[%s] Buffering DONE is detected !!\n", GST_OBJECT_NAME(GST_MESSAGE_SRC(message)));
999                         player->streamer->buffering_state |= MM_PLAYER_BUFFERING_COMPLETE;
1000                 }
1001
1002                 MMPLAYER_CMD_UNLOCK(player);
1003
1004                 break;
1005         }
1006         default:
1007                 retval = FALSE;
1008                 break;
1009         }
1010
1011         return retval;
1012 }
1013
1014 static void
1015 __mmplayer_update_buffer_setting(mm_player_t *player, GstMessage *buffering_msg)
1016 {
1017         MMHandleType attrs = 0;
1018         guint64 data_size = 0;
1019         gchar* path = NULL;
1020         gint64 pos_nsec = 0;
1021         struct stat sb;
1022
1023         MMPLAYER_RETURN_IF_FAIL(player && player->pipeline && player->pipeline->mainbin);
1024
1025         __mmplayer_gst_get_position(player, MM_PLAYER_POS_FORMAT_TIME, &pos_nsec);      /* to update player->last_position */
1026
1027         attrs = MMPLAYER_GET_ATTRS(player);
1028         if (!attrs) {
1029                 LOGE("fail to get attributes.\n");
1030                 return;
1031         }
1032
1033         if (!MMPLAYER_IS_STREAMING(player) && (player->can_support_codec & FOUND_PLUGIN_VIDEO)) {
1034                 mm_attrs_get_string_by_name(attrs, "profile_uri", &path);
1035
1036                 if (stat(path, &sb) == 0)
1037                         data_size = (guint64)sb.st_size;
1038         } else if (MMPLAYER_IS_HTTP_STREAMING(player))
1039                 data_size = player->http_content_size;
1040
1041         __mm_player_streaming_buffering(player->streamer, buffering_msg, data_size, player->last_position, player->duration);
1042         __mm_player_streaming_sync_property(player->streamer, player->pipeline->mainbin[MMPLAYER_M_AUTOPLUG].gst);
1043
1044         return;
1045 }
1046
1047 static int
1048 __mmplayer_handle_buffering_message(mm_player_t* player)
1049 {
1050         int ret = MM_ERROR_NONE;
1051         MMPlayerStateType prev_state = MM_PLAYER_STATE_NONE;
1052         MMPlayerStateType current_state = MM_PLAYER_STATE_NONE;
1053         MMPlayerStateType target_state = MM_PLAYER_STATE_NONE;
1054         MMPlayerStateType pending_state = MM_PLAYER_STATE_NONE;
1055
1056         if (!player || !player->streamer || (MMPLAYER_IS_LIVE_STREAMING(player) && MMPLAYER_IS_RTSP_STREAMING(player))) {
1057                 LOGW("do nothing for buffering msg\n");
1058                 ret = MM_ERROR_PLAYER_INVALID_STATE;
1059                 goto exit;
1060         }
1061
1062         prev_state = MMPLAYER_PREV_STATE(player);
1063         current_state = MMPLAYER_CURRENT_STATE(player);
1064         target_state = MMPLAYER_TARGET_STATE(player);
1065         pending_state = MMPLAYER_PENDING_STATE(player);
1066
1067         LOGD("player state : prev %s, current %s, pending %s, target %s, buffering state 0x%X",
1068                 MMPLAYER_STATE_GET_NAME(prev_state),
1069                 MMPLAYER_STATE_GET_NAME(current_state),
1070                 MMPLAYER_STATE_GET_NAME(pending_state),
1071                 MMPLAYER_STATE_GET_NAME(target_state),
1072                 player->streamer->buffering_state);
1073
1074         if (!(player->streamer->buffering_state & MM_PLAYER_BUFFERING_IN_PROGRESS)) {
1075                 /* NOTE : if buffering has done, player has to go to target state. */
1076                 switch (target_state) {
1077                 case MM_PLAYER_STATE_PAUSED:
1078                         {
1079                                 switch (pending_state) {
1080                                 case MM_PLAYER_STATE_PLAYING:
1081                                         __mmplayer_gst_pause(player, TRUE);
1082                                         break;
1083
1084                                 case MM_PLAYER_STATE_PAUSED:
1085                                         LOGD("player is already going to paused state, there is nothing to do.\n");
1086                                         break;
1087
1088                                 case MM_PLAYER_STATE_NONE:
1089                                 case MM_PLAYER_STATE_NULL:
1090                                 case MM_PLAYER_STATE_READY:
1091                                 default:
1092                                         LOGW("invalid pending state [%s].\n", MMPLAYER_STATE_GET_NAME(pending_state));
1093                                         break;
1094                                 }
1095                         }
1096                         break;
1097
1098                 case MM_PLAYER_STATE_PLAYING:
1099                         {
1100                                 switch (pending_state) {
1101                                 case MM_PLAYER_STATE_NONE:
1102                                         {
1103                                                 if (current_state != MM_PLAYER_STATE_PLAYING)
1104                                                         __mmplayer_gst_resume(player, TRUE);
1105                                         }
1106                                         break;
1107
1108                                 case MM_PLAYER_STATE_PAUSED:
1109                                         /* NOTE: It should be worked as asynchronously.
1110                                          * Because, buffering can be completed during autoplugging when pipeline would try to go playing state directly.
1111                                          */
1112                                         if (current_state == MM_PLAYER_STATE_PLAYING) {
1113                                                 /* NOTE: If the current state is PLAYING, it means, async __mmplayer_gst_pause() is not completed yet.
1114                                                  * The current state should be changed to paused purposely to prevent state conflict.
1115                                                  */
1116                                                 MMPLAYER_SET_STATE(player, MM_PLAYER_STATE_PAUSED);
1117                                         }
1118                                         __mmplayer_gst_resume(player, TRUE);
1119                                         break;
1120
1121                                 case MM_PLAYER_STATE_PLAYING:
1122                                         LOGD("player is already going to playing state, there is nothing to do.\n");
1123                                         break;
1124
1125                                 case MM_PLAYER_STATE_NULL:
1126                                 case MM_PLAYER_STATE_READY:
1127                                 default:
1128                                         LOGW("invalid pending state [%s].\n", MMPLAYER_STATE_GET_NAME(pending_state));
1129                                         break;
1130                                 }
1131                         }
1132                         break;
1133
1134                 case MM_PLAYER_STATE_NULL:
1135                 case MM_PLAYER_STATE_READY:
1136                 case MM_PLAYER_STATE_NONE:
1137                 default:
1138                         LOGW("invalid target state [%s].\n", MMPLAYER_STATE_GET_NAME(target_state));
1139                         break;
1140                 }
1141         } else {
1142                 /* NOTE : during the buffering, pause the player for stopping pipeline clock.
1143                  *      it's for stopping the pipeline clock to prevent dropping the data in sink element.
1144                  */
1145                 switch (pending_state) {
1146                 case MM_PLAYER_STATE_NONE:
1147                         {
1148                                 if (current_state != MM_PLAYER_STATE_PAUSED) {
1149                                         /* rtsp streaming pause makes rtsp server stop sending data. */
1150                                         if (!MMPLAYER_IS_RTSP_STREAMING(player)) {
1151                                                 LOGD("set pause state during buffering\n");
1152                                                 __mmplayer_gst_pause(player, TRUE);
1153                                         }
1154                                 }
1155                         }
1156                         break;
1157
1158                 case MM_PLAYER_STATE_PLAYING:
1159                         /* rtsp streaming pause makes rtsp server stop sending data. */
1160                         if (!MMPLAYER_IS_RTSP_STREAMING(player))
1161                                 __mmplayer_gst_pause(player, TRUE);
1162                         break;
1163
1164                 case MM_PLAYER_STATE_PAUSED:
1165                         break;
1166
1167                 case MM_PLAYER_STATE_NULL:
1168                 case MM_PLAYER_STATE_READY:
1169                 default:
1170                         LOGW("invalid pending state [%s].\n", MMPLAYER_STATE_GET_NAME(pending_state));
1171                         break;
1172                 }
1173         }
1174
1175 exit:
1176         return ret;
1177 }
1178
1179 static VariantData *
1180 __mmplayer_adaptive_var_info(const VariantData *self, gpointer user_data)
1181 {
1182         VariantData *var_info = NULL;
1183         g_return_val_if_fail(self != NULL, NULL);
1184
1185         var_info = g_new0(VariantData, 1);
1186         if (!var_info) return NULL;
1187         var_info->bandwidth = self->bandwidth;
1188         var_info->width = self->width;
1189         var_info->height = self->height;
1190         return var_info;
1191 }
1192
1193 static gboolean
1194 __mmplayer_gst_handle_duration(mm_player_t* player, GstMessage* msg)
1195 {
1196         gint64 bytes = 0;
1197
1198         MMPLAYER_FENTER();
1199
1200         MMPLAYER_RETURN_VAL_IF_FAIL(player, FALSE);
1201         MMPLAYER_RETURN_VAL_IF_FAIL(msg, FALSE);
1202
1203         if ((MMPLAYER_IS_HTTP_STREAMING(player)) &&
1204                 (msg->src) && (msg->src == (GstObject *)player->pipeline->mainbin[MMPLAYER_M_SRC].gst)) {
1205                 LOGD("msg src : [%s]", GST_ELEMENT_NAME(GST_ELEMENT_CAST(msg->src)));
1206
1207                 if (gst_element_query_duration(GST_ELEMENT_CAST(msg->src), GST_FORMAT_BYTES, &bytes)) {
1208                         LOGD("data total size of http content: %"G_GINT64_FORMAT, bytes);
1209                         player->http_content_size = (bytes > 0) ? (bytes) : (0);
1210                 }
1211         } else {
1212                 /* handling audio clip which has vbr. means duration is keep changing */
1213                 __mmplayer_update_content_attrs(player, ATTR_DURATION);
1214         }
1215
1216         MMPLAYER_FLEAVE();
1217
1218         return TRUE;
1219 }
1220
1221 static gboolean
1222 __mmplayer_eos_timer_cb(gpointer u_data)
1223 {
1224         mm_player_t* player = NULL;
1225         MMHandleType attrs = 0;
1226         int count = 0;
1227
1228         MMPLAYER_RETURN_VAL_IF_FAIL(u_data, FALSE);
1229
1230         player = (mm_player_t*) u_data;
1231         attrs = MMPLAYER_GET_ATTRS(player);
1232
1233         mm_attrs_get_int_by_name(attrs, "profile_play_count", &count);
1234
1235         if (count == -1) {
1236                 gint ret_value = 0;
1237                 ret_value = __mmplayer_gst_set_position(player, MM_PLAYER_POS_FORMAT_TIME, 0, TRUE);
1238                 if (ret_value != MM_ERROR_NONE)
1239                         LOGE("seeking to 0 failed in repeat play");
1240         } else {
1241                 /* posting eos */
1242                 MMPLAYER_POST_MSG(player, MM_MESSAGE_END_OF_STREAM, NULL);
1243         }
1244
1245         /* we are returning FALSE as we need only one posting */
1246         return FALSE;
1247 }
1248
1249 static void
1250 __mmplayer_handle_eos_delay(mm_player_t* player, int delay_in_ms)
1251 {
1252         MMPLAYER_RETURN_IF_FAIL(player);
1253
1254         /* post now if delay is zero */
1255         if (delay_in_ms == 0 || player->set_mode.pcm_extraction) {
1256                 LOGD("eos delay is zero. posting EOS now\n");
1257                 MMPLAYER_POST_MSG(player, MM_MESSAGE_END_OF_STREAM, NULL);
1258
1259                 if (player->set_mode.pcm_extraction)
1260                         __mmplayer_cancel_eos_timer(player);
1261
1262                 return;
1263         }
1264
1265         /* cancel if existing */
1266         __mmplayer_cancel_eos_timer(player);
1267
1268         /* init new timeout */
1269         /* NOTE : consider give high priority to this timer */
1270         LOGD("posting EOS message after [%d] msec\n", delay_in_ms);
1271
1272         player->eos_timer = g_timeout_add(delay_in_ms,
1273                 __mmplayer_eos_timer_cb, player);
1274
1275         player->context.global_default = g_main_context_default();
1276         LOGD("global default context = %p, eos timer id = %d", player->context.global_default, player->eos_timer);
1277
1278         /* check timer is valid. if not, send EOS now */
1279         if (player->eos_timer == 0) {
1280                 LOGW("creating timer for delayed EOS has failed. sending EOS now\n");
1281                 MMPLAYER_POST_MSG(player, MM_MESSAGE_END_OF_STREAM, NULL);
1282         }
1283 }
1284
1285 static int __mmplayer_gst_pending_seek(mm_player_t* player)
1286 {
1287         MMPlayerStateType current_state = MM_PLAYER_STATE_NONE;
1288         int ret = MM_ERROR_NONE;
1289
1290         MMPLAYER_FENTER();
1291
1292         MMPLAYER_RETURN_VAL_IF_FAIL(player && player->pipeline, MM_ERROR_PLAYER_NOT_INITIALIZED);
1293
1294         if (!player->pending_seek.is_pending) {
1295                 LOGD("pending seek is not reserved. nothing to do.\n");
1296                 return ret;
1297         }
1298
1299         /* check player state if player could pending seek or not. */
1300         current_state = MMPLAYER_CURRENT_STATE(player);
1301
1302         if (current_state != MM_PLAYER_STATE_PAUSED && current_state != MM_PLAYER_STATE_PLAYING) {
1303                 LOGW("try to pending seek in %s state, try next time. \n",
1304                         MMPLAYER_STATE_GET_NAME(current_state));
1305                 return ret;
1306         }
1307
1308         LOGD("trying to play from(%"G_GINT64_FORMAT") pending position\n", player->pending_seek.pos);
1309
1310         ret = __mmplayer_gst_set_position(player, player->pending_seek.format, player->pending_seek.pos, FALSE);
1311
1312         if (MM_ERROR_NONE != ret)
1313                 LOGE("failed to seek pending postion. just keep staying current position.\n");
1314
1315         player->pending_seek.is_pending = FALSE;
1316
1317         MMPLAYER_FLEAVE();
1318
1319         return ret;
1320 }
1321
1322 static void
1323 __mmplayer_gst_handle_async(mm_player_t* player, gboolean async, enum MMPlayerSinkType type)
1324 {
1325         MMPlayerGstElement *videobin = NULL, *audiobin = NULL, *textbin = NULL;
1326
1327         MMPLAYER_RETURN_IF_FAIL(player && player->pipeline);
1328
1329         audiobin = player->pipeline->audiobin; /* can be null */
1330         videobin = player->pipeline->videobin; /* can be null */
1331         textbin = player->pipeline->textbin;   /* can be null */
1332
1333         LOGD("Async will be set to %d about 0x%X type sink", async, type);
1334
1335         if ((type & MMPLAYER_AUDIO_SINK) && audiobin && audiobin[MMPLAYER_A_SINK].gst)
1336                 g_object_set(audiobin[MMPLAYER_A_SINK].gst, "async", async, NULL);
1337
1338         if ((type & MMPLAYER_VIDEO_SINK) && videobin && videobin[MMPLAYER_V_SINK].gst)
1339                 g_object_set(videobin[MMPLAYER_V_SINK].gst, "async", async, NULL);
1340
1341         if ((type & MMPLAYER_TEXT_SINK) && textbin && textbin[MMPLAYER_T_FAKE_SINK].gst)
1342                 g_object_set(textbin[MMPLAYER_T_FAKE_SINK].gst, "async", async, NULL);
1343
1344         return;
1345 }
1346
1347 static void
1348 __mmplayer_drop_subtitle(mm_player_t* player, gboolean is_drop)
1349 {
1350         MMPlayerGstElement *textbin;
1351         MMPLAYER_FENTER();
1352
1353         MMPLAYER_RETURN_IF_FAIL(player &&
1354                                         player->pipeline &&
1355                                         player->pipeline->textbin);
1356
1357         MMPLAYER_RETURN_IF_FAIL(player->pipeline->textbin[MMPLAYER_T_IDENTITY].gst);
1358
1359         textbin = player->pipeline->textbin;
1360
1361         if (is_drop) {
1362                 LOGD("Drop subtitle text after getting EOS\n");
1363
1364                 __mmplayer_gst_handle_async(player, FALSE, MMPLAYER_TEXT_SINK);
1365                 g_object_set(textbin[MMPLAYER_T_IDENTITY].gst, "drop-probability", (gfloat)1.0, NULL);
1366
1367                 player->is_subtitle_force_drop = TRUE;
1368         } else {
1369                 if (player->is_subtitle_force_drop == TRUE) {
1370                         LOGD("Enable subtitle data path without drop\n");
1371
1372                         g_object_set(textbin[MMPLAYER_T_IDENTITY].gst, "drop-probability", (gfloat)0.0, NULL);
1373                         __mmplayer_gst_handle_async(player, TRUE, MMPLAYER_TEXT_SINK);
1374
1375                         LOGD("non-connected with external display");
1376
1377                         player->is_subtitle_force_drop = FALSE;
1378                 }
1379         }
1380 }
1381
1382
1383 #if 0
1384 #endif
1385
1386 int
1387 __mmplayer_gst_set_state(mm_player_t* player, GstElement * element,  GstState state, gboolean async, gint timeout)
1388 {
1389         GstState element_state = GST_STATE_VOID_PENDING;
1390         GstState element_pending_state = GST_STATE_VOID_PENDING;
1391         GstStateChangeReturn ret = GST_STATE_CHANGE_FAILURE;
1392
1393         MMPLAYER_FENTER();
1394
1395         MMPLAYER_RETURN_VAL_IF_FAIL(player, MM_ERROR_PLAYER_NOT_INITIALIZED);
1396         MMPLAYER_RETURN_VAL_IF_FAIL(element, MM_ERROR_INVALID_ARGUMENT);
1397
1398         LOGD("setting [%s] element state to : %s\n", GST_ELEMENT_NAME(element), gst_element_state_get_name(state));
1399
1400         /* set state */
1401         ret = gst_element_set_state(element, state);
1402
1403         if (ret == GST_STATE_CHANGE_FAILURE) {
1404                 LOGE("failed to set [%s] state\n", GST_ELEMENT_NAME(element));
1405
1406                 /* dump state of all element */
1407                 __mmplayer_dump_pipeline_state(player);
1408
1409                 return MM_ERROR_PLAYER_INTERNAL;
1410         }
1411
1412         /* return here so state transition to be done in async mode */
1413         if (async) {
1414                 LOGD("async state transition. not waiting for state complete.\n");
1415                 return MM_ERROR_NONE;
1416         }
1417
1418         /* wait for state transition */
1419         ret = gst_element_get_state(element, &element_state, &element_pending_state, timeout * GST_SECOND);
1420
1421         if (ret == GST_STATE_CHANGE_FAILURE || (state != element_state)) {
1422                 LOGE("failed to change [%s] element state to [%s] within %d sec\n",
1423                         GST_ELEMENT_NAME(element),
1424                         gst_element_state_get_name(state), timeout);
1425
1426                 LOGE(" [%s] state : %s   pending : %s \n",
1427                         GST_ELEMENT_NAME(element),
1428                         gst_element_state_get_name(element_state),
1429                         gst_element_state_get_name(element_pending_state));
1430
1431                 /* dump state of all element */
1432                 __mmplayer_dump_pipeline_state(player);
1433
1434                 return MM_ERROR_PLAYER_INTERNAL;
1435         }
1436
1437         LOGD("[%s] element state has changed\n", GST_ELEMENT_NAME(element));
1438
1439         MMPLAYER_FLEAVE();
1440
1441         return MM_ERROR_NONE;
1442 }
1443
1444 void
1445 __mmplayer_gst_callback(GstMessage *msg, gpointer data)
1446 {
1447         mm_player_t* player = (mm_player_t*)(data);
1448
1449         MMPLAYER_RETURN_IF_FAIL(player);
1450         MMPLAYER_RETURN_IF_FAIL(msg && GST_IS_MESSAGE(msg));
1451
1452         switch (GST_MESSAGE_TYPE(msg)) {
1453         case GST_MESSAGE_UNKNOWN:
1454                 LOGD("unknown message received\n");
1455                 break;
1456
1457         case GST_MESSAGE_EOS:
1458                 {
1459                         MMHandleType attrs = 0;
1460                         gint count = 0;
1461
1462                         LOGD("GST_MESSAGE_EOS received\n");
1463
1464                         /* NOTE : EOS event is comming multiple time. watch out it */
1465                         /* check state. we only process EOS when pipeline state goes to PLAYING */
1466                         if (!(player->cmd == MMPLAYER_COMMAND_START || player->cmd == MMPLAYER_COMMAND_RESUME)) {
1467                                 LOGD("EOS received on non-playing state. ignoring it\n");
1468                                 break;
1469                         }
1470
1471                         if (player->pipeline) {
1472                                 if (player->pipeline->textbin)
1473                                         __mmplayer_drop_subtitle(player, TRUE);
1474
1475                                 if ((player->audio_stream_cb) && (player->set_mode.pcm_extraction) && (!player->audio_stream_render_cb_ex)) {
1476                                         GstPad *pad = NULL;
1477
1478                                         pad = gst_element_get_static_pad(player->pipeline->audiobin[MMPLAYER_A_SINK].gst, "sink");
1479
1480                                         LOGD("release audio callback\n");
1481
1482                                         /* release audio callback */
1483                                         gst_pad_remove_probe(pad, player->audio_cb_probe_id);
1484                                         player->audio_cb_probe_id = 0;
1485                                         /* audio callback should be free because it can be called even though probe remove.*/
1486                                         player->audio_stream_cb = NULL;
1487                                         player->audio_stream_cb_user_param = NULL;
1488
1489                                 }
1490                         }
1491                         if ((player->audio_stream_render_cb_ex) && (!player->audio_stream_sink_sync))
1492                                 __mmplayer_audio_stream_clear_buffer(player, TRUE);
1493
1494                         /* rewind if repeat count is greater then zero */
1495                         /* get play count */
1496                         attrs = MMPLAYER_GET_ATTRS(player);
1497
1498                         if (attrs) {
1499                                 mm_attrs_get_int_by_name(attrs, "profile_play_count", &count);
1500
1501                                 LOGD("play count: %d, playback rate: %f\n", count, player->playback_rate);
1502
1503                                 if (count == -1 || player->playback_rate < 0.0) /* default value is 1 */ {
1504                                         if (player->playback_rate < 0.0) {
1505                                                 player->resumed_by_rewind = TRUE;
1506                                                 _mmplayer_set_mute((MMHandleType)player, 0);
1507                                                 MMPLAYER_POST_MSG(player, MM_MESSAGE_RESUMED_BY_REW, NULL);
1508                                         }
1509
1510                                         __mmplayer_handle_eos_delay(player, player->ini.delay_before_repeat);
1511
1512                                         /* initialize */
1513                                         player->sent_bos = FALSE;
1514
1515                                         /* not posting eos when repeating */
1516                                         break;
1517                                 }
1518                         }
1519
1520                         if (player->pipeline)
1521                                 MMPLAYER_GENERATE_DOT_IF_ENABLED(player, "pipeline-status-eos");
1522
1523                         /* post eos message to application */
1524                         __mmplayer_handle_eos_delay(player, player->ini.eos_delay);
1525
1526                         /* reset last position */
1527                         player->last_position = 0;
1528                 }
1529                 break;
1530
1531         case GST_MESSAGE_ERROR:
1532                 {
1533                         GError *error = NULL;
1534                         gchar* debug = NULL;
1535
1536                         /* generating debug info before returning error */
1537                         MMPLAYER_GENERATE_DOT_IF_ENABLED(player, "pipeline-status-error");
1538
1539                         /* get error code */
1540                         gst_message_parse_error(msg, &error, &debug);
1541
1542                         if (gst_structure_has_name(gst_message_get_structure(msg), "streaming_error")) {
1543                                 /* Note : the streaming error from the streaming source is handled
1544                                  *   using __mmplayer_handle_streaming_error.
1545                                  */
1546                                 __mmplayer_handle_streaming_error(player, msg);
1547
1548                                 /* dump state of all element */
1549                                 __mmplayer_dump_pipeline_state(player);
1550                         } else {
1551                                 /* traslate gst error code to msl error code. then post it
1552                                  * to application if needed
1553                                  */
1554                                 __mmplayer_handle_gst_error(player, msg, error);
1555
1556                                 if (debug)
1557                                         LOGE("error debug : %s", debug);
1558                         }
1559
1560                         if (MMPLAYER_IS_HTTP_PD(player))
1561                                 _mmplayer_unrealize_pd_downloader((MMHandleType)player);
1562
1563                         MMPLAYER_FREEIF(debug);
1564                         g_error_free(error);
1565                 }
1566                 break;
1567
1568         case GST_MESSAGE_WARNING:
1569                 {
1570                         char* debug = NULL;
1571                         GError* error = NULL;
1572
1573                         gst_message_parse_warning(msg, &error, &debug);
1574
1575                         LOGD("warning : %s\n", error->message);
1576                         LOGD("debug : %s\n", debug);
1577
1578                         MMPLAYER_POST_MSG(player, MM_MESSAGE_WARNING, NULL);
1579
1580                         MMPLAYER_FREEIF(debug);
1581                         g_error_free(error);
1582                 }
1583                 break;
1584
1585         case GST_MESSAGE_TAG:
1586                 {
1587                         LOGD("GST_MESSAGE_TAG\n");
1588                         if (!__mmplayer_gst_extract_tag_from_msg(player, msg))
1589                                 LOGW("failed to extract tags from gstmessage\n");
1590                 }
1591                 break;
1592
1593         case GST_MESSAGE_BUFFERING:
1594                 {
1595                         MMMessageParamType msg_param = {0, };
1596                         int bRet = MM_ERROR_NONE;
1597
1598                         if (!(player->pipeline && player->pipeline->mainbin)) {
1599                                 LOGE("Pipeline is not initialized");
1600                                 break;
1601                         }
1602
1603                         if (!MMPLAYER_IS_STREAMING(player))
1604                                 break;
1605
1606                         if (player->pd_mode == MM_PLAYER_PD_MODE_URI) {
1607                                 if (!MMPLAYER_CMD_TRYLOCK(player)) {
1608                                         /* skip the playback control by buffering msg while user request is handled. */
1609                                         gint per = 0;
1610
1611                                         LOGW("[PD mode] can't get cmd lock, only post buffering msg");
1612
1613                                         gst_message_parse_buffering(msg, &per);
1614                                         LOGD("[PD mode][%s] buffering %d %%....", GST_OBJECT_NAME(GST_MESSAGE_SRC(msg)), per);
1615
1616                                         msg_param.connection.buffering = per;
1617                                         MMPLAYER_POST_MSG(player, MM_MESSAGE_BUFFERING, &msg_param);
1618                                         break;
1619                                 }
1620                         } else {
1621                                 MMPLAYER_CMD_LOCK(player);
1622                         }
1623
1624                         if (!player->streamer) {
1625                                 LOGW("Pipeline is shutting down");
1626                                 MMPLAYER_CMD_UNLOCK(player);
1627                                 break;
1628                         }
1629
1630                         /* ignore the remained buffering message till getting 100% msg */
1631                         if (player->streamer->buffering_state == MM_PLAYER_BUFFERING_COMPLETE) {
1632                                 gint buffer_percent = 0;
1633
1634                                 gst_message_parse_buffering(msg, &buffer_percent);
1635
1636                                 if (buffer_percent == MAX_BUFFER_PERCENT) {
1637                                         LOGD("Ignored all the previous buffering msg!(got %d%%)\n", buffer_percent);
1638                                         player->streamer->buffering_state = MM_PLAYER_BUFFERING_DEFAULT;
1639                                 }
1640                                 MMPLAYER_CMD_UNLOCK(player);
1641                                 break;
1642                         }
1643
1644                         /* ignore the remained buffering message */
1645                         if (player->streamer->buffering_state == MM_PLAYER_BUFFERING_ABORT) {
1646                                 gint buffer_percent = 0;
1647
1648                                 gst_message_parse_buffering(msg, &buffer_percent);
1649
1650                                 LOGD("interrupted buffering -last posted %d %%, new per %d %%",
1651                                                         player->streamer->buffering_percent, buffer_percent);
1652
1653                                 if (player->streamer->buffering_percent > buffer_percent || buffer_percent <= 0) {
1654                                         player->streamer->buffering_state = MM_PLAYER_BUFFERING_DEFAULT;
1655                                         player->streamer->buffering_req.is_pre_buffering = FALSE;
1656
1657                                         LOGD("interrupted buffering - need to enter the buffering mode again - %d %%", buffer_percent);
1658                                 } else {
1659                                         LOGD("interrupted buffering - ignored the remained buffering msg!");
1660                                         MMPLAYER_CMD_UNLOCK(player);
1661                                         break;
1662                                 }
1663                         }
1664
1665                         __mmplayer_update_buffer_setting(player, msg);
1666
1667                         bRet = __mmplayer_handle_buffering_message(player); /* playback control */
1668
1669                         if (bRet == MM_ERROR_NONE) {
1670                                 msg_param.connection.buffering = player->streamer->buffering_percent;
1671                                 MMPLAYER_POST_MSG(player, MM_MESSAGE_BUFFERING, &msg_param);
1672
1673                                 if (MMPLAYER_IS_RTSP_STREAMING(player) &&
1674                                         player->pending_resume &&
1675                                         (player->streamer->buffering_percent >= MAX_BUFFER_PERCENT)) {
1676
1677                                         player->is_external_subtitle_added_now = FALSE;
1678                                         player->pending_resume = FALSE;
1679                                         _mmplayer_resume((MMHandleType)player);
1680                                 }
1681
1682                                 if (MMPLAYER_IS_RTSP_STREAMING(player) &&
1683                                         (player->streamer->buffering_percent >= MAX_BUFFER_PERCENT)) {
1684
1685                                         if (player->seek_state == MMPLAYER_SEEK_IN_PROGRESS) {
1686                                                 if (MMPLAYER_TARGET_STATE(player) == MM_PLAYER_STATE_PAUSED) {
1687                                                         player->seek_state = MMPLAYER_SEEK_NONE;
1688                                                         MMPLAYER_POST_MSG(player, MM_MESSAGE_SEEK_COMPLETED, NULL);
1689                                                 } else if (MMPLAYER_TARGET_STATE(player) == MM_PLAYER_STATE_PLAYING) {
1690                                                         /* Considering the async state trasition in case of RTSP.
1691                                                            After getting state change gst msg, seek cmpleted msg will be posted. */
1692                                                         player->seek_state = MMPLAYER_SEEK_COMPLETED;
1693                                                 }
1694                                         }
1695                                 }
1696                         } else if (bRet == MM_ERROR_PLAYER_INVALID_STATE) {
1697                                 if (!player->streamer) {
1698                                         LOGW("player->streamer is NULL, so discarding the buffering percent update\n");
1699                                         MMPLAYER_CMD_UNLOCK(player);
1700                                         break;
1701                                 }
1702
1703                                 if ((MMPLAYER_IS_LIVE_STREAMING(player)) && (MMPLAYER_IS_RTSP_STREAMING(player))) {
1704
1705                                         LOGD("player->last_position=%"G_GINT64_FORMAT" , player->streamer->buffering_percent=%d \n",
1706                                                         GST_TIME_AS_SECONDS(player->last_position), player->streamer->buffering_percent);
1707
1708                                         if ((GST_TIME_AS_SECONDS(player->last_position) <= 0) && (MMPLAYER_CURRENT_STATE(player) == MM_PLAYER_STATE_PAUSED)) {
1709                                                 msg_param.connection.buffering = player->streamer->buffering_percent;
1710                                                 MMPLAYER_POST_MSG(player, MM_MESSAGE_BUFFERING, &msg_param);
1711                                         } else {
1712                                                 LOGD("Not updating Buffering Message for Live RTSP case !!!\n");
1713                                         }
1714                                 } else {
1715                                         msg_param.connection.buffering = player->streamer->buffering_percent;
1716                                         MMPLAYER_POST_MSG(player, MM_MESSAGE_BUFFERING, &msg_param);
1717                                 }
1718                         }
1719                         MMPLAYER_CMD_UNLOCK(player);
1720                 }
1721                 break;
1722
1723         case GST_MESSAGE_STATE_CHANGED:
1724                 {
1725                         MMPlayerGstElement *mainbin;
1726                         const GValue *voldstate, *vnewstate, *vpending;
1727                         GstState oldstate = GST_STATE_NULL;
1728                         GstState newstate = GST_STATE_NULL;
1729                         GstState pending = GST_STATE_NULL;
1730
1731                         if (!(player->pipeline && player->pipeline->mainbin)) {
1732                                 LOGE("player pipeline handle is null");
1733                                 break;
1734                         }
1735
1736                         mainbin = player->pipeline->mainbin;
1737
1738                         /* we only handle messages from pipeline */
1739                         if (msg->src != (GstObject *)mainbin[MMPLAYER_M_PIPE].gst)
1740                                 break;
1741
1742                         /* get state info from msg */
1743                         voldstate = gst_structure_get_value(gst_message_get_structure(msg), "old-state");
1744                         vnewstate = gst_structure_get_value(gst_message_get_structure(msg), "new-state");
1745                         vpending = gst_structure_get_value(gst_message_get_structure(msg), "pending-state");
1746
1747                         if (!voldstate || !vnewstate) {
1748                                 LOGE("received msg has wrong format.");
1749                                 break;
1750                         }
1751
1752                         oldstate = (GstState)voldstate->data[0].v_int;
1753                         newstate = (GstState)vnewstate->data[0].v_int;
1754                         if (vpending)
1755                                 pending = (GstState)vpending->data[0].v_int;
1756
1757                         LOGD("state changed [%s] : %s ---> %s     final : %s\n",
1758                                 GST_OBJECT_NAME(GST_MESSAGE_SRC(msg)),
1759                                 gst_element_state_get_name((GstState)oldstate),
1760                                 gst_element_state_get_name((GstState)newstate),
1761                                 gst_element_state_get_name((GstState)pending));
1762
1763                         if (newstate == GST_STATE_PLAYING) {
1764                                 if ((MMPLAYER_IS_RTSP_STREAMING(player)) && (player->pending_seek.is_pending)) {
1765
1766                                         int retVal = MM_ERROR_NONE;
1767                                         LOGD("trying to play from (%"G_GINT64_FORMAT") pending position\n", player->pending_seek.pos);
1768
1769                                         retVal = __mmplayer_gst_set_position(player, player->pending_seek.format, player->pending_seek.pos, TRUE);
1770
1771                                         if (MM_ERROR_NONE != retVal)
1772                                                 LOGE("failed to seek pending postion. just keep staying current position.\n");
1773
1774                                         player->pending_seek.is_pending = FALSE;
1775                                 }
1776                         }
1777
1778                         if (oldstate == newstate) {
1779                                 LOGD("pipeline reports state transition to old state");
1780                                 break;
1781                         }
1782
1783                         switch (newstate) {
1784                         case GST_STATE_VOID_PENDING:
1785                                 break;
1786
1787                         case GST_STATE_NULL:
1788                                 break;
1789
1790                         case GST_STATE_READY:
1791                                 break;
1792
1793                         case GST_STATE_PAUSED:
1794                                 {
1795                                         gboolean prepare_async = FALSE;
1796
1797                                         if (!player->audio_cb_probe_id && player->set_mode.pcm_extraction && !player->audio_stream_render_cb_ex)
1798                                                 __mmplayer_configure_audio_callback(player);
1799
1800                                         if (!player->sent_bos && oldstate == GST_STATE_READY) {
1801                                                 // managed prepare async case
1802                                                 mm_attrs_get_int_by_name(player->attrs, "profile_prepare_async", &prepare_async);
1803                                                 LOGD("checking prepare mode for async transition - %d", prepare_async);
1804                                         }
1805
1806                                         if (MMPLAYER_IS_STREAMING(player) || MMPLAYER_IS_MS_BUFF_SRC(player) || prepare_async) {
1807                                                 MMPLAYER_SET_STATE(player, MM_PLAYER_STATE_PAUSED);
1808
1809                                                 if (MMPLAYER_IS_STREAMING(player) && (player->streamer))
1810                                                         __mm_player_streaming_set_content_bitrate(player->streamer,
1811                                                                 player->total_maximum_bitrate, player->total_bitrate);
1812
1813                                                 if (player->pending_seek.is_pending) {
1814                                                         LOGW("trying to do pending seek");
1815                                                         MMPLAYER_CMD_LOCK(player);
1816                                                         __mmplayer_gst_pending_seek(player);
1817                                                         MMPLAYER_CMD_UNLOCK(player);
1818                                                 }
1819                                         }
1820                                 }
1821                                 break;
1822
1823                         case GST_STATE_PLAYING:
1824                                 {
1825                                         if (MMPLAYER_IS_STREAMING(player)) {
1826                                                 // managed prepare async case when buffering is completed
1827                                                 // pending state should be reset otherwise, it's still playing even though it's resumed after bufferging.
1828                                                 if ((MMPLAYER_CURRENT_STATE(player) != MM_PLAYER_STATE_PLAYING) ||
1829                                                         (MMPLAYER_PENDING_STATE(player) == MM_PLAYER_STATE_PLAYING))
1830                                                         MMPLAYER_SET_STATE(player, MM_PLAYER_STATE_PLAYING);
1831
1832                                                 if (MMPLAYER_IS_RTSP_STREAMING(player) && (MMPLAYER_IS_LIVE_STREAMING(player))) {
1833
1834                                                         LOGD("Current Buffering Percent = %d", player->streamer->buffering_percent);
1835                                                         if (player->streamer->buffering_percent < 100) {
1836
1837                                                                 MMMessageParamType msg_param = {0, };
1838                                                                 LOGW("Posting Buffering Completed Message to Application !!!");
1839
1840                                                                 msg_param.connection.buffering = 100;
1841                                                                 MMPLAYER_POST_MSG(player, MM_MESSAGE_BUFFERING, &msg_param);
1842                                                         }
1843                                                 }
1844                                         }
1845
1846                                         if (player->gapless.stream_changed) {
1847                                                 __mmplayer_update_content_attrs(player, ATTR_ALL);
1848                                                 player->gapless.stream_changed = FALSE;
1849                                         }
1850
1851                                         if (player->seek_state == MMPLAYER_SEEK_COMPLETED) {
1852                                                 player->seek_state = MMPLAYER_SEEK_NONE;
1853                                                 MMPLAYER_POST_MSG(player, MM_MESSAGE_SEEK_COMPLETED, NULL);
1854                                         }
1855                                 }
1856                                 break;
1857
1858                         default:
1859                                 break;
1860                         }
1861                 }
1862                 break;
1863
1864         case GST_MESSAGE_CLOCK_LOST:
1865                         {
1866                                 GstClock *clock = NULL;
1867                                 gboolean need_new_clock = FALSE;
1868
1869                                 gst_message_parse_clock_lost(msg, &clock);
1870                                 LOGD("GST_MESSAGE_CLOCK_LOST : %s\n", (clock ? GST_OBJECT_NAME(clock) : "NULL"));
1871
1872                                 if (!player->videodec_linked)
1873                                         need_new_clock = TRUE;
1874                                 else if (!player->ini.use_system_clock)
1875                                         need_new_clock = TRUE;
1876
1877                                 if (need_new_clock) {
1878                                         LOGD("Provide clock is TRUE, do pause->resume\n");
1879                                         __mmplayer_gst_pause(player, FALSE);
1880                                         __mmplayer_gst_resume(player, FALSE);
1881                                 }
1882                         }
1883                         break;
1884
1885         case GST_MESSAGE_NEW_CLOCK:
1886                         {
1887                                 GstClock *clock = NULL;
1888                                 gst_message_parse_new_clock(msg, &clock);
1889                                 LOGD("GST_MESSAGE_NEW_CLOCK : %s\n", (clock ? GST_OBJECT_NAME(clock) : "NULL"));
1890                         }
1891                         break;
1892
1893         case GST_MESSAGE_ELEMENT:
1894                         {
1895                                 const gchar *structure_name;
1896                                 gint count = 0, idx = 0;
1897                                 MMHandleType attrs = 0;
1898
1899                                 attrs = MMPLAYER_GET_ATTRS(player);
1900                                 if (!attrs) {
1901                                         LOGE("cannot get content attribute");
1902                                         break;
1903                                 }
1904
1905                                 if (gst_message_get_structure(msg) == NULL)
1906                                         break;
1907
1908                                 structure_name = gst_structure_get_name(gst_message_get_structure(msg));
1909                                 if (!structure_name)
1910                                         break;
1911
1912                                 LOGD("GST_MESSAGE_ELEMENT %s from %s", structure_name, GST_OBJECT_NAME(GST_MESSAGE_SRC(msg)));
1913
1914                                 if (!strcmp(structure_name, "adaptive-streaming-variant")) {
1915                                         const GValue *var_info = NULL;
1916
1917                                         var_info = gst_structure_get_value(gst_message_get_structure(msg), "video-variant-info");
1918                                         if (var_info != NULL) {
1919                                                 if (player->adaptive_info.var_list)
1920                                                         g_list_free_full(player->adaptive_info.var_list, g_free);
1921
1922                                                 /* share addr or copy the list */
1923                                                 player->adaptive_info.var_list =
1924                                                         g_list_copy_deep((GList *)g_value_get_pointer(var_info), (GCopyFunc)__mmplayer_adaptive_var_info, NULL);
1925
1926                                                 count = g_list_length(player->adaptive_info.var_list);
1927                                                 if (count > 0) {
1928                                                         VariantData *temp = NULL;
1929
1930                                                         /* print out for debug */
1931                                                         LOGD("num of variant_info %d", count);
1932                                                         for (idx = 0; idx < count; idx++) {
1933                                                                 temp = g_list_nth_data(player->adaptive_info.var_list, idx);
1934                                                                 if (temp)
1935                                                                         LOGD("variant(%d) [b]%d [w]%d [h]%d ", idx, temp->bandwidth, temp->width, temp->height);
1936                                                         }
1937                                                 }
1938                                         }
1939                                 }
1940
1941                                 if (!strcmp(structure_name, "prepare-decode-buffers")) {
1942                                         gint num_buffers = 0;
1943                                         gint extra_num_buffers = 0;
1944
1945                                         if (gst_structure_get_int(gst_message_get_structure(msg), "num_buffers", &num_buffers)) {
1946                                                 player->video_num_buffers = num_buffers;
1947                                                 LOGD("video_num_buffers : %d", player->video_num_buffers);
1948                                         }
1949
1950                                         if (gst_structure_get_int(gst_message_get_structure(msg), "extra_num_buffers", &extra_num_buffers)) {
1951                                                 player->video_extra_num_buffers = extra_num_buffers;
1952                                                 LOGD("num_of_vout_extra num buffers : %d", extra_num_buffers);
1953                                         }
1954                                         break;
1955                                 }
1956
1957                                 if (!strcmp(structure_name, "Language_list")) {
1958                                         const GValue *lang_list = NULL;
1959                                         lang_list = gst_structure_get_value(gst_message_get_structure(msg), "lang_list");
1960                                         if (lang_list != NULL) {
1961                                                 count = g_list_length((GList *)g_value_get_pointer(lang_list));
1962                                                 if (count > 1)
1963                                                         LOGD("Total audio tracks(from parser) = %d \n", count);
1964                                         }
1965                                 }
1966
1967                                 if (!strcmp(structure_name, "Ext_Sub_Language_List")) {
1968                                         const GValue *lang_list = NULL;
1969                                         MMPlayerLangStruct *temp = NULL;
1970
1971                                         lang_list = gst_structure_get_value(gst_message_get_structure(msg), "lang_list");
1972                                         if (lang_list != NULL) {
1973                                                 count = g_list_length((GList *)g_value_get_pointer(lang_list));
1974                                                 if (count) {
1975                                                         MMPLAYER_SUBTITLE_INFO_LOCK(player);
1976                                                         player->subtitle_language_list = (GList *)g_value_get_pointer(lang_list);
1977                                                         mm_attrs_set_int_by_name(attrs, "content_text_track_num", (gint)count);
1978                                                         if (mmf_attrs_commit(attrs))
1979                                                                 LOGE("failed to commit.\n");
1980                                                         LOGD("Total subtitle tracks = %d \n", count);
1981
1982                                                         while (count) {
1983                                                                 temp = g_list_nth_data(player->subtitle_language_list, count - 1);
1984                                                                 if (temp)
1985                                                                         LOGD("value of lang_key is %s and lang_code is %s",
1986                                                                                                 temp->language_key, temp->language_code);
1987                                                                 count--;
1988                                                         }
1989                                                         MMPLAYER_SUBTITLE_INFO_SIGNAL(player);
1990                                                         MMPLAYER_SUBTITLE_INFO_UNLOCK(player);
1991                                                 }
1992                                         }
1993                                 }
1994
1995                                 /* custom message */
1996                                 if (!strcmp(structure_name, "audio_codec_not_supported")) {
1997                                         MMMessageParamType msg_param = {0,};
1998                                         msg_param.code = MM_ERROR_PLAYER_AUDIO_CODEC_NOT_FOUND;
1999                                         MMPLAYER_POST_MSG(player, MM_MESSAGE_ERROR, &msg_param);
2000                                 }
2001
2002                                 /* custom message for RTSP attribute :
2003                                     RTSP case, buffer is not come from server before PLAYING state. However,we have to get attribute after PAUSE state chaged.
2004                                     sdp which has contents info is received when rtsp connection is opened.
2005                                     extract duration ,codec info , resolution from sdp and get it by GstMessage */
2006                                 if (!strcmp(structure_name, "rtspsrc_properties")) {
2007
2008                                         gchar *audio_codec = NULL;
2009                                         gchar *video_codec = NULL;
2010                                         gchar *video_frame_size = NULL;
2011
2012                                         gst_structure_get(gst_message_get_structure(msg), "rtsp_duration", G_TYPE_UINT64, &player->duration, NULL);
2013                                         LOGD("rtsp duration : %"G_GINT64_FORMAT" msec", GST_TIME_AS_MSECONDS(player->duration));
2014                                         player->streaming_type = __mmplayer_get_stream_service_type(player);
2015
2016                                         gst_structure_get(gst_message_get_structure(msg), "rtsp_audio_codec", G_TYPE_STRING, &audio_codec, NULL);
2017                                         LOGD("rtsp_audio_codec : %s", audio_codec);
2018                                         if (audio_codec)
2019                                                 mm_attrs_set_string_by_name(player->attrs, "content_audio_codec", audio_codec);
2020
2021                                         gst_structure_get(gst_message_get_structure(msg), "rtsp_video_codec", G_TYPE_STRING, &video_codec, NULL);
2022                                         LOGD("rtsp_video_codec : %s", video_codec);
2023                                         if (video_codec)
2024                                                 mm_attrs_set_string_by_name(player->attrs, "content_video_codec", video_codec);
2025
2026                                         gst_structure_get(gst_message_get_structure(msg), "rtsp_video_frame_size", G_TYPE_STRING, &video_frame_size, NULL);
2027                                         LOGD("rtsp_video_frame_size : %s", video_frame_size);
2028                                         if (video_frame_size) {
2029
2030                                                 char *seperator = strchr(video_frame_size, '-');
2031                                                 if (seperator) {
2032
2033                                                         char video_width[10] = {0,};
2034                                                         int frame_size_len = strlen(video_frame_size);
2035                                                         int separtor_len = strlen(seperator);
2036
2037                                                         strncpy(video_width, video_frame_size, (frame_size_len - separtor_len));
2038                                                         mm_attrs_set_int_by_name(attrs, "content_video_width", atoi(video_width));
2039
2040                                                         seperator++;
2041                                                         mm_attrs_set_int_by_name(attrs, "content_video_height", atoi(seperator));
2042                                                 }
2043                                         }
2044
2045                                         if (mmf_attrs_commit(attrs))
2046                                                 LOGE("failed to commit.\n");
2047                                 }
2048                         }
2049                         break;
2050
2051         case GST_MESSAGE_DURATION_CHANGED:
2052                 {
2053                         LOGD("GST_MESSAGE_DURATION_CHANGED\n");
2054                         if (!__mmplayer_gst_handle_duration(player, msg))
2055                                 LOGW("failed to update duration");
2056                 }
2057
2058                 break;
2059
2060         case GST_MESSAGE_ASYNC_START:
2061                         LOGD("GST_MESSAGE_ASYNC_START : %s\n", GST_ELEMENT_NAME(GST_MESSAGE_SRC(msg)));
2062                 break;
2063
2064         case GST_MESSAGE_ASYNC_DONE:
2065                 {
2066                         MMPlayerGstElement *mainbin;
2067
2068                         if (!(player->pipeline && player->pipeline->mainbin)) {
2069                                 LOGE("player pipeline handle is null");
2070                                 break;
2071                         }
2072
2073                         mainbin = player->pipeline->mainbin;
2074
2075                         LOGD("GST_MESSAGE_ASYNC_DONE : %s\n", GST_ELEMENT_NAME(GST_MESSAGE_SRC(msg)));
2076
2077                         /* we only handle messages from pipeline */
2078                         if (msg->src != (GstObject *)mainbin[MMPLAYER_M_PIPE].gst)
2079                                 break;
2080
2081                         if (player->seek_state == MMPLAYER_SEEK_IN_PROGRESS) {
2082                                 if (MMPLAYER_TARGET_STATE(player) == MM_PLAYER_STATE_PAUSED) {
2083                                         player->seek_state = MMPLAYER_SEEK_NONE;
2084                                         MMPLAYER_POST_MSG(player, MM_MESSAGE_SEEK_COMPLETED, NULL);
2085                                 } else if (MMPLAYER_TARGET_STATE(player) == MM_PLAYER_STATE_PLAYING) {
2086                                         if (mainbin[MMPLAYER_M_AUTOPLUG].gst) {
2087                                                 LOGD("sync %s state(%s) with parent state(%s)",
2088                                                         GST_ELEMENT_NAME(mainbin[MMPLAYER_M_AUTOPLUG].gst),
2089                                                         gst_element_state_get_name(GST_STATE(mainbin[MMPLAYER_M_AUTOPLUG].gst)),
2090                                                         gst_element_state_get_name(GST_STATE(mainbin[MMPLAYER_M_PIPE].gst)));
2091
2092                                                 /* In case of streaming, pause is required before finishing seeking by buffering.
2093                                                    After completing the seek(during buffering), the player and sink elems has paused state but others in playing state.
2094                                                    Because the buffering state is controlled according to the state transition for force resume,
2095                                                    the decodebin state should be paused as player state. */
2096                                                 gst_element_sync_state_with_parent(mainbin[MMPLAYER_M_AUTOPLUG].gst);
2097                                         }
2098
2099                                         if ((MMPLAYER_IS_HTTP_STREAMING(player)) &&
2100                                                 (player->streamer) &&
2101                                                 (player->streamer->streaming_buffer_type == BUFFER_TYPE_MUXED) &&
2102                                                 !(player->streamer->buffering_state & MM_PLAYER_BUFFERING_IN_PROGRESS)) {
2103                                                 GstQuery *query = NULL;
2104                                                 gboolean busy = FALSE;
2105                                                 gint percent = 0;
2106
2107                                                 if (player->streamer->buffer_handle[BUFFER_TYPE_MUXED].buffer) {
2108                                                         query = gst_query_new_buffering(GST_FORMAT_PERCENT);
2109                                                         if (gst_element_query(player->streamer->buffer_handle[BUFFER_TYPE_MUXED].buffer, query))
2110                                                                 gst_query_parse_buffering_percent(query, &busy, &percent);
2111                                                         gst_query_unref(query);
2112
2113                                                         LOGD("buffered percent(%s): %d\n",
2114                                                                 GST_ELEMENT_NAME(player->streamer->buffer_handle[BUFFER_TYPE_MUXED].buffer), percent);
2115                                                 }
2116
2117                                                 if (percent >= 100)
2118                                                         __mmplayer_handle_buffering_message(player);
2119                                         }
2120
2121                                         player->seek_state = MMPLAYER_SEEK_COMPLETED;
2122                                 }
2123                         }
2124                 }
2125                 break;
2126
2127         #if 0 /* delete unnecessary logs */
2128         case GST_MESSAGE_REQUEST_STATE:         LOGD("GST_MESSAGE_REQUEST_STATE\n"); break;
2129         case GST_MESSAGE_STEP_START:            LOGD("GST_MESSAGE_STEP_START\n"); break;
2130         case GST_MESSAGE_QOS:                           LOGD("GST_MESSAGE_QOS\n"); break;
2131         case GST_MESSAGE_PROGRESS:                      LOGD("GST_MESSAGE_PROGRESS\n"); break;
2132         case GST_MESSAGE_ANY:                           LOGD("GST_MESSAGE_ANY\n"); break;
2133         case GST_MESSAGE_INFO:                          LOGD("GST_MESSAGE_STATE_DIRTY\n"); break;
2134         case GST_MESSAGE_STATE_DIRTY:           LOGD("GST_MESSAGE_STATE_DIRTY\n"); break;
2135         case GST_MESSAGE_STEP_DONE:                     LOGD("GST_MESSAGE_STEP_DONE\n"); break;
2136         case GST_MESSAGE_CLOCK_PROVIDE:         LOGD("GST_MESSAGE_CLOCK_PROVIDE\n"); break;
2137         case GST_MESSAGE_STRUCTURE_CHANGE:      LOGD("GST_MESSAGE_STRUCTURE_CHANGE\n"); break;
2138         case GST_MESSAGE_STREAM_STATUS:         LOGD("GST_MESSAGE_STREAM_STATUS\n"); break;
2139         case GST_MESSAGE_APPLICATION:           LOGD("GST_MESSAGE_APPLICATION\n"); break;
2140         case GST_MESSAGE_SEGMENT_START:         LOGD("GST_MESSAGE_SEGMENT_START\n"); break;
2141         case GST_MESSAGE_SEGMENT_DONE:          LOGD("GST_MESSAGE_SEGMENT_DONE\n"); break;
2142         case GST_MESSAGE_LATENCY:                               LOGD("GST_MESSAGE_LATENCY\n"); break;
2143         #endif
2144
2145         default:
2146                 break;
2147         }
2148
2149         /* should not call 'gst_message_unref(msg)' */
2150         return;
2151 }
2152
2153 GstBusSyncReply
2154 __mmplayer_bus_sync_callback(GstBus * bus, GstMessage * message, gpointer data)
2155 {
2156         mm_player_t *player = (mm_player_t *)data;
2157         GstBusSyncReply reply = GST_BUS_DROP;
2158
2159         if (!(player->pipeline && player->pipeline->mainbin)) {
2160                 LOGE("player pipeline handle is null");
2161                 return GST_BUS_PASS;
2162         }
2163
2164         if (!__mmplayer_gst_check_useful_message(player, message)) {
2165                 gst_message_unref(message);
2166                 return GST_BUS_DROP;
2167         }
2168
2169         switch (GST_MESSAGE_TYPE(message)) {
2170         case GST_MESSAGE_STATE_CHANGED:
2171                 /* post directly for fast launch */
2172                 if (player->sync_handler) {
2173                         __mmplayer_gst_callback(message, player);
2174                         reply = GST_BUS_DROP;
2175                 } else
2176                         reply = GST_BUS_PASS;
2177                 break;
2178         case GST_MESSAGE_TAG:
2179                 __mmplayer_gst_extract_tag_from_msg(player, message);
2180
2181                 #if 0 // debug
2182                 {
2183                         GstTagList *tags = NULL;
2184
2185                         gst_message_parse_tag(message, &tags);
2186                         if (tags) {
2187                                 LOGE("TAGS received from element \"%s\".\n",
2188                                 GST_STR_NULL(GST_ELEMENT_NAME(GST_MESSAGE_SRC(message))));
2189
2190                                 gst_tag_list_foreach(tags, print_tag, NULL);
2191                                 gst_tag_list_free(tags);
2192                                 tags = NULL;
2193                         }
2194                         break;
2195                 }
2196                 #endif
2197                 break;
2198
2199         case GST_MESSAGE_DURATION_CHANGED:
2200                 __mmplayer_gst_handle_duration(player, message);
2201                 break;
2202         case GST_MESSAGE_ASYNC_DONE:
2203                 /* NOTE:Don't call gst_callback directly
2204                  * because previous frame can be showed even though this message is received for seek.
2205                  */
2206         default:
2207                 reply = GST_BUS_PASS;
2208                 break;
2209         }
2210
2211         if (reply == GST_BUS_DROP)
2212                 gst_message_unref(message);
2213
2214         return reply;
2215 }
2216
2217 int __mmplayer_gst_start(mm_player_t* player)
2218 {
2219         int ret = MM_ERROR_NONE;
2220         gboolean async = FALSE;
2221
2222         MMPLAYER_FENTER();
2223
2224         MMPLAYER_RETURN_VAL_IF_FAIL(player && player->pipeline, MM_ERROR_PLAYER_NOT_INITIALIZED);
2225
2226         /* NOTE : if SetPosition was called before Start. do it now */
2227         /* streaming doesn't support it. so it should be always sync */
2228         /* !!create one more api to check if there is pending seek rather than checking variables */
2229         if (player->pending_seek.is_pending && !MMPLAYER_IS_STREAMING(player)) {
2230                 MMPLAYER_TARGET_STATE(player) = MM_PLAYER_STATE_PAUSED;
2231                 ret = __mmplayer_gst_pause(player, FALSE);
2232                 if (ret != MM_ERROR_NONE) {
2233                         LOGE("failed to set state to PAUSED for pending seek");
2234                         return ret;
2235                 }
2236
2237                 MMPLAYER_TARGET_STATE(player) = MM_PLAYER_STATE_PLAYING;
2238                 if (__mmplayer_gst_pending_seek(player) != MM_ERROR_NONE)
2239                                 LOGW("failed to seek pending postion. starting from the begin of content");
2240         }
2241
2242         LOGD("current state before doing transition");
2243         MMPLAYER_PENDING_STATE(player) = MM_PLAYER_STATE_PLAYING;
2244         MMPLAYER_PRINT_STATE(player);
2245
2246         /* set pipeline state to PLAYING  */
2247         ret = __mmplayer_gst_set_state(player,
2248                 player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, GST_STATE_PLAYING, async, MMPLAYER_STATE_CHANGE_TIMEOUT(player));
2249
2250         if (ret == MM_ERROR_NONE) {
2251                 MMPLAYER_SET_STATE(player, MM_PLAYER_STATE_PLAYING);
2252         } else {
2253                 LOGE("failed to set state to PLAYING");
2254                 return ret;
2255         }
2256
2257         /* generating debug info before returning error */
2258         MMPLAYER_GENERATE_DOT_IF_ENABLED(player, "pipeline-status-start");
2259
2260         MMPLAYER_FLEAVE();
2261
2262         return ret;
2263 }
2264
2265 int __mmplayer_gst_stop(mm_player_t* player)
2266 {
2267         GstStateChangeReturn change_ret = GST_STATE_CHANGE_SUCCESS;
2268         MMHandleType attrs = 0;
2269         gboolean rewind = FALSE;
2270         gint timeout = 0;
2271         int ret = MM_ERROR_NONE;
2272
2273         MMPLAYER_FENTER();
2274
2275         MMPLAYER_RETURN_VAL_IF_FAIL(player && player->pipeline, MM_ERROR_PLAYER_NOT_INITIALIZED);
2276         MMPLAYER_RETURN_VAL_IF_FAIL(player->pipeline->mainbin, MM_ERROR_PLAYER_NOT_INITIALIZED);
2277
2278         LOGD("current state before doing transition");
2279         MMPLAYER_PENDING_STATE(player) = MM_PLAYER_STATE_READY;
2280         MMPLAYER_PRINT_STATE(player);
2281
2282         attrs = MMPLAYER_GET_ATTRS(player);
2283         if (!attrs) {
2284                 LOGE("cannot get content attribute\n");
2285                 return MM_ERROR_PLAYER_INTERNAL;
2286         }
2287
2288         /* Just set state to PAUESED and the rewind. it's usual player behavior. */
2289         timeout = MMPLAYER_STATE_CHANGE_TIMEOUT(player);
2290
2291         if ((!MMPLAYER_IS_STREAMING(player) && !MMPLAYER_IS_MS_BUFF_SRC(player)) ||
2292                 (player->streaming_type == STREAMING_SERVICE_VOD && player->videodec_linked))
2293                 rewind = TRUE;
2294
2295         if (player->es_player_push_mode || MMPLAYER_IS_HTTP_PD(player)) {
2296                 /* disable the async state transition because there could be no data in the pipeline */
2297                 __mmplayer_gst_handle_async(player, FALSE, MMPLAYER_SINK_ALL);
2298         }
2299
2300         /* set gst state */
2301         ret = __mmplayer_gst_set_state(player, player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, GST_STATE_PAUSED, FALSE, timeout);
2302
2303         if (player->es_player_push_mode || MMPLAYER_IS_HTTP_PD(player)) {
2304                 /* enable the async state transition as default operation */
2305                 __mmplayer_gst_handle_async(player, TRUE, MMPLAYER_SINK_ALL);
2306         }
2307
2308         /* return if set_state has failed */
2309         if (ret != MM_ERROR_NONE) {
2310                 LOGE("failed to set state.\n");
2311                 return ret;
2312         }
2313
2314         /* rewind */
2315         if (rewind) {
2316                 if (!__mmplayer_gst_seek(player, player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, player->playback_rate,
2317                                 GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, GST_SEEK_TYPE_SET, 0,
2318                                 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE)) {
2319                         LOGW("failed to rewind\n");
2320                         ret = MM_ERROR_PLAYER_SEEK;
2321                 }
2322         }
2323
2324         /* initialize */
2325         player->sent_bos = FALSE;
2326
2327         if (player->es_player_push_mode) //for cloudgame
2328                 timeout = 0;
2329
2330         /* wait for seek to complete */
2331         change_ret = gst_element_get_state(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, NULL, NULL, timeout * GST_SECOND);
2332         if (change_ret == GST_STATE_CHANGE_SUCCESS || change_ret == GST_STATE_CHANGE_NO_PREROLL) {
2333                 MMPLAYER_SET_STATE(player, MM_PLAYER_STATE_READY);
2334         } else {
2335                 LOGE("fail to stop player.\n");
2336                 ret = MM_ERROR_PLAYER_INTERNAL;
2337                 __mmplayer_dump_pipeline_state(player);
2338         }
2339
2340         /* generate dot file if enabled */
2341         MMPLAYER_GENERATE_DOT_IF_ENABLED(player, "pipeline-status-stop");
2342
2343         MMPLAYER_FLEAVE();
2344
2345         return ret;
2346 }
2347
2348 int __mmplayer_gst_pause(mm_player_t* player, gboolean async)
2349 {
2350         int ret = MM_ERROR_NONE;
2351
2352         MMPLAYER_FENTER();
2353
2354         MMPLAYER_RETURN_VAL_IF_FAIL(player && player->pipeline, MM_ERROR_PLAYER_NOT_INITIALIZED);
2355         MMPLAYER_RETURN_VAL_IF_FAIL(player->pipeline->mainbin, MM_ERROR_PLAYER_NOT_INITIALIZED);
2356
2357         LOGD("current state before doing transition");
2358         MMPLAYER_PENDING_STATE(player) = MM_PLAYER_STATE_PAUSED;
2359         MMPLAYER_PRINT_STATE(player);
2360
2361         /* set pipeline status to PAUSED */
2362         ret = __mmplayer_gst_set_state(player,
2363                 player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, GST_STATE_PAUSED, async, MMPLAYER_STATE_CHANGE_TIMEOUT(player));
2364
2365         if (FALSE == async) {
2366                 if (ret != MM_ERROR_NONE) {
2367                         GstMessage *msg = NULL;
2368                         GTimer *timer = NULL;
2369                         gdouble MAX_TIMEOUT_SEC = 3;
2370
2371                         LOGE("failed to set state to PAUSED");
2372
2373                         if (!player->bus_watcher) {
2374                                 LOGE("there is no bus msg thread. pipeline is shutting down.");
2375                                 return ret;
2376                         }
2377
2378                         if (player->msg_posted) {
2379                                 LOGE("error msg is already posted.");
2380                                 return ret;
2381                         }
2382
2383                         timer = g_timer_new();
2384                         g_timer_start(timer);
2385
2386                         GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst));
2387
2388                         do {
2389                                 msg = gst_bus_timed_pop(bus, 100 * GST_MSECOND);
2390                                 if (msg) {
2391                                         if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ERROR) {
2392                                                 GError *error = NULL;
2393
2394                                                 /* parse error code */
2395                                                 gst_message_parse_error(msg, &error, NULL);
2396
2397                                                 if (gst_structure_has_name(gst_message_get_structure(msg), "streaming_error")) {
2398                                                         /* Note : the streaming error from the streaming source is handled
2399                                                          *   using __mmplayer_handle_streaming_error.
2400                                                          */
2401                                                         __mmplayer_handle_streaming_error(player, msg);
2402
2403                                                 } else if (error) {
2404                                                         LOGE("paring error posted from bus, domain : %s, code : %d", g_quark_to_string(error->domain), error->code);
2405
2406                                                         if (error->domain == GST_STREAM_ERROR)
2407                                                                 ret = __mmplayer_gst_handle_stream_error(player, error, msg);
2408                                                         else if (error->domain == GST_RESOURCE_ERROR)
2409                                                                 ret = __mmplayer_gst_handle_resource_error(player, error->code, NULL);
2410                                                         else if (error->domain == GST_LIBRARY_ERROR)
2411                                                                 ret = __mmplayer_gst_handle_library_error(player, error->code);
2412                                                         else if (error->domain == GST_CORE_ERROR)
2413                                                                 ret = __mmplayer_gst_handle_core_error(player, error->code);
2414
2415                                                         g_error_free(error);
2416                                                 }
2417                                                 player->msg_posted = TRUE;
2418                                         }
2419                                         gst_message_unref(msg);
2420                                 }
2421                         } while (!player->msg_posted && (g_timer_elapsed(timer, NULL) < MAX_TIMEOUT_SEC));
2422                         /* clean */
2423                         gst_object_unref(bus);
2424                         g_timer_stop(timer);
2425                         g_timer_destroy(timer);
2426
2427                         return ret;
2428
2429                 } else if ((!MMPLAYER_IS_RTSP_STREAMING(player)) && (!player->video_stream_cb) &&
2430                                    (!player->pipeline->videobin) && (!player->pipeline->audiobin)) {
2431
2432                         return MM_ERROR_PLAYER_CODEC_NOT_FOUND;
2433
2434                 } else {
2435                         MMPLAYER_SET_STATE(player, MM_PLAYER_STATE_PAUSED);
2436                 }
2437         }
2438
2439         /* generate dot file before returning error */
2440         MMPLAYER_GENERATE_DOT_IF_ENABLED(player, "pipeline-status-pause");
2441
2442         MMPLAYER_FLEAVE();
2443
2444         return ret;
2445 }
2446
2447 int __mmplayer_gst_resume(mm_player_t* player, gboolean async)
2448 {
2449         int ret = MM_ERROR_NONE;
2450         gint timeout = 0;
2451
2452         MMPLAYER_FENTER();
2453
2454         MMPLAYER_RETURN_VAL_IF_FAIL(player && player->pipeline,
2455                 MM_ERROR_PLAYER_NOT_INITIALIZED);
2456
2457         LOGD("current state before doing transition");
2458         MMPLAYER_PENDING_STATE(player) = MM_PLAYER_STATE_PLAYING;
2459         MMPLAYER_PRINT_STATE(player);
2460
2461         if (async)
2462                 LOGD("do async state transition to PLAYING");
2463
2464         /* set pipeline state to PLAYING */
2465         timeout = MMPLAYER_STATE_CHANGE_TIMEOUT(player);
2466
2467         ret = __mmplayer_gst_set_state(player,
2468                 player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, GST_STATE_PLAYING, async, timeout);
2469         if (ret != MM_ERROR_NONE) {
2470                 LOGE("failed to set state to PLAYING");
2471                 goto EXIT;
2472         } else {
2473                 if (async == FALSE)
2474                         MMPLAYER_SET_STATE(player, MM_PLAYER_STATE_PLAYING);
2475         }
2476
2477 EXIT:
2478         /* generate dot file */
2479         MMPLAYER_GENERATE_DOT_IF_ENABLED(player, "pipeline-status-resume");
2480
2481         MMPLAYER_FLEAVE();
2482
2483         return ret;
2484 }
2485
2486 /* sending event to one of sinkelements */
2487 gboolean
2488 __mmplayer_gst_send_event_to_sink(mm_player_t* player, GstEvent* event)
2489 {
2490         GstEvent * event2 = NULL;
2491         GList *sinks = NULL;
2492         gboolean res = FALSE;
2493         MMPLAYER_FENTER();
2494
2495         MMPLAYER_RETURN_VAL_IF_FAIL(player, FALSE);
2496         MMPLAYER_RETURN_VAL_IF_FAIL(event, FALSE);
2497
2498         /* While adding subtitles in live feeds seek is getting called.
2499            Adding defensive check in framework layer.*/
2500         if (GST_EVENT_TYPE(event) == GST_EVENT_SEEK) {
2501                 if (MMPLAYER_IS_LIVE_STREAMING(player)) {
2502                         LOGE("Should not send seek event during live playback");
2503                         return TRUE;
2504                 }
2505         }
2506
2507         if (player->play_subtitle)
2508                 event2 = gst_event_copy((const GstEvent *)event);
2509
2510         sinks = player->sink_elements;
2511         while (sinks) {
2512                 GstElement *sink = GST_ELEMENT_CAST(sinks->data);
2513
2514                 if (GST_IS_ELEMENT(sink)) {
2515                         /* keep ref to the event */
2516                         gst_event_ref(event);
2517
2518                         if ((res = gst_element_send_event(sink, event))) {
2519                                 LOGD("sending event[%s] to sink element [%s] success!\n",
2520                                         GST_EVENT_TYPE_NAME(event), GST_ELEMENT_NAME(sink));
2521
2522                                 /* rtsp case, asyn_done is not called after seek during pause state */
2523                                 if (MMPLAYER_IS_RTSP_STREAMING(player)) {
2524                                         if (GST_EVENT_TYPE(event) == GST_EVENT_SEEK) {
2525                                                 if (MMPLAYER_TARGET_STATE(player) == MM_PLAYER_STATE_PAUSED) {
2526                                                         LOGD("RTSP seek completed, after pause state..\n");
2527                                                         player->seek_state = MMPLAYER_SEEK_NONE;
2528                                                         MMPLAYER_POST_MSG(player, MM_MESSAGE_SEEK_COMPLETED, NULL);
2529                                                 }
2530
2531                                         }
2532                                 }
2533
2534                                 if (MMPLAYER_IS_MS_BUFF_SRC(player)) {
2535                                         sinks = g_list_next(sinks);
2536                                         continue;
2537                                 } else {
2538                                         break;
2539                                 }
2540                         }
2541
2542                         LOGD("sending event[%s] to sink element [%s] failed. try with next one.\n",
2543                                 GST_EVENT_TYPE_NAME(event), GST_ELEMENT_NAME(sink));
2544                 }
2545
2546                 sinks = g_list_next(sinks);
2547         }
2548
2549         /* Note : Textbin is not linked to the video or audio bin.
2550          * It needs to send the event to the text sink seperatelly.
2551          */
2552          if (player->play_subtitle && player->pipeline) {
2553                 GstElement *text_sink = GST_ELEMENT_CAST(player->pipeline->textbin[MMPLAYER_T_FAKE_SINK].gst);
2554
2555                 if (GST_IS_ELEMENT(text_sink)) {
2556                         /* keep ref to the event */
2557                         gst_event_ref(event2);
2558
2559                         if ((res = gst_element_send_event(text_sink, event2)))
2560                                 LOGD("sending event[%s] to subtitle sink element [%s] success!\n",
2561                                         GST_EVENT_TYPE_NAME(event2), GST_ELEMENT_NAME(text_sink));
2562                         else
2563                                 LOGE("sending event[%s] to subtitle sink element [%s] failed!\n",
2564                                         GST_EVENT_TYPE_NAME(event2), GST_ELEMENT_NAME(text_sink));
2565
2566                         gst_event_unref(event2);
2567                 }
2568          }
2569
2570         gst_event_unref(event);
2571
2572         MMPLAYER_FLEAVE();
2573
2574         return res;
2575 }
2576
2577 gboolean
2578 __mmplayer_gst_seek(mm_player_t* player, GstElement * element, gdouble rate,
2579                         GstFormat format, GstSeekFlags flags, GstSeekType cur_type,
2580                         gint64 cur, GstSeekType stop_type, gint64 stop)
2581 {
2582         GstEvent* event = NULL;
2583         gboolean result = FALSE;
2584
2585         MMPLAYER_FENTER();
2586
2587         MMPLAYER_RETURN_VAL_IF_FAIL(player, FALSE);
2588
2589         if (player->pipeline && player->pipeline->textbin)
2590                 __mmplayer_drop_subtitle(player, FALSE);
2591
2592         event = gst_event_new_seek(rate, format, flags, cur_type,
2593                 cur, stop_type, stop);
2594
2595         result = __mmplayer_gst_send_event_to_sink(player, event);
2596
2597         MMPLAYER_FLEAVE();
2598
2599         return result;
2600 }
2601
2602 int
2603 __mmplayer_gst_set_position(mm_player_t* player, int format, gint64 position, gboolean internal_called)
2604 {
2605         gint64 dur_nsec = 0;
2606         gint64 pos_nsec = 0;
2607         gboolean ret = TRUE;
2608         gboolean accurated = FALSE;
2609         GstSeekFlags seek_flags = GST_SEEK_FLAG_FLUSH;
2610
2611         MMPLAYER_FENTER();
2612         MMPLAYER_RETURN_VAL_IF_FAIL(player && player->pipeline, MM_ERROR_PLAYER_NOT_INITIALIZED);
2613         MMPLAYER_RETURN_VAL_IF_FAIL(!MMPLAYER_IS_LIVE_STREAMING(player), MM_ERROR_PLAYER_NO_OP);
2614
2615         if (MMPLAYER_CURRENT_STATE(player) != MM_PLAYER_STATE_PLAYING
2616                 && MMPLAYER_CURRENT_STATE(player) != MM_PLAYER_STATE_PAUSED)
2617                 goto PENDING;
2618
2619         if (!MMPLAYER_IS_MS_BUFF_SRC(player)) {
2620                 /* check duration */
2621                 /* NOTE : duration cannot be zero except live streaming.
2622                  *              Since some element could have some timing problemn with quering duration, try again.
2623                  */
2624                 if (player->duration == 0) {
2625                         if (!gst_element_query_duration(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, GST_FORMAT_TIME, &dur_nsec)) {
2626                                 /* For RTSP Streaming , duration is not returned in READY state. So seek to the previous position does not work properly.
2627                                  * Added a patch to postpone the actual seek when state changes to PLAY. Sending a fake SEEK_COMPLETED event to finish the current request. */
2628                                 if ((MMPLAYER_IS_RTSP_STREAMING(player)) && (__mmplayer_get_stream_service_type(player) == STREAMING_SERVICE_VOD)) {
2629                                         player->pending_seek.is_pending = TRUE;
2630                                         player->pending_seek.format = format;
2631                                         player->pending_seek.pos = position;
2632                                         player->seek_state = MMPLAYER_SEEK_NONE;
2633                                         MMPLAYER_POST_MSG(player, MM_MESSAGE_SEEK_COMPLETED, NULL);
2634                                         return MM_ERROR_NONE;
2635                                 } else {
2636                                         goto SEEK_ERROR;
2637                                 }
2638                         }
2639                         player->duration = dur_nsec;
2640                 }
2641         }
2642         LOGD("playback rate: %f\n", player->playback_rate);
2643
2644         mm_attrs_get_int_by_name(player->attrs, "accurate_seek", &accurated);
2645         if (accurated)
2646                 seek_flags |= GST_SEEK_FLAG_ACCURATE;
2647         else
2648                 seek_flags |= GST_SEEK_FLAG_KEY_UNIT;
2649
2650         /* do seek */
2651         switch (format) {
2652         case MM_PLAYER_POS_FORMAT_TIME:
2653         {
2654                 if (!MMPLAYER_IS_MS_BUFF_SRC(player)) {
2655                         GstQuery *query = NULL;
2656                         gboolean seekable = FALSE;
2657
2658                         /* check position is valid or not */
2659                         if (position > player->duration)
2660                                 goto INVALID_ARGS;
2661
2662                         query = gst_query_new_seeking(GST_FORMAT_TIME);
2663                         if (gst_element_query(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, query)) {
2664                                 gst_query_parse_seeking(query, NULL, &seekable, NULL, NULL);
2665                                 gst_query_unref(query);
2666
2667                                 if (!seekable) {
2668                                         LOGW("non-seekable content");
2669                                         player->seek_state = MMPLAYER_SEEK_NONE;
2670                                         return MM_ERROR_PLAYER_NO_OP;
2671                                 }
2672                         } else {
2673                                 LOGW("failed to get seeking query");
2674                                 gst_query_unref(query); /* keep seeking operation */
2675                         }
2676
2677                         LOGD("seeking to(%"G_GINT64_FORMAT") nsec, duration is %"G_GINT64_FORMAT" nsec\n", position, player->duration);
2678
2679                         /* For rtspsrc stack , npt-start value coming from server is used for finding the current position.
2680                            But when a rtsp clip (especially from Youtube Desktop View) is paused and kept for sometime,npt-start is still increasing.
2681                            This causes problem is position calculation during normal pause resume scenarios also.
2682                            Currently during seek , we are sending the current position to rtspsrc module for position saving for later use. */
2683                         if ((MMPLAYER_IS_RTSP_STREAMING(player)) &&
2684                                 (__mmplayer_get_stream_service_type(player) == STREAMING_SERVICE_VOD)) {
2685                                 if (!gst_element_query_position(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, GST_FORMAT_TIME, &pos_nsec))
2686                                         LOGW("getting current position failed in seek\n");
2687
2688                                 player->last_position = pos_nsec;
2689                                 g_object_set(player->pipeline->mainbin[MMPLAYER_M_SRC].gst, "resume-position", player->last_position, NULL);
2690                         }
2691
2692                         if (player->seek_state != MMPLAYER_SEEK_NONE) {
2693                                 LOGD("not completed seek");
2694                                 return MM_ERROR_PLAYER_DOING_SEEK;
2695                         }
2696                 }
2697
2698                 if (!internal_called)
2699                         player->seek_state = MMPLAYER_SEEK_IN_PROGRESS;
2700
2701                 if ((MMPLAYER_IS_HTTP_STREAMING(player)) && (!player->videodec_linked)) {
2702                         gint64 cur_time = 0;
2703
2704                         /* get current position */
2705                         gst_element_query_position(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, GST_FORMAT_TIME, &cur_time);
2706
2707                         /* flush */
2708                         GstEvent *event = gst_event_new_seek(1.0,
2709                                                         GST_FORMAT_TIME,
2710                                                         (GstSeekFlags)GST_SEEK_FLAG_FLUSH,
2711                                                         GST_SEEK_TYPE_SET, cur_time,
2712                                                         GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
2713                         if (event)
2714                                 __mmplayer_gst_send_event_to_sink(player, event);
2715
2716                         if (!MMPLAYER_IS_RTSP_STREAMING(player))
2717                                 __mmplayer_gst_pause(player, FALSE);
2718                 }
2719
2720                 pos_nsec = position;
2721
2722                 /* rtsp streaming case, there is no sink after READY TO PAUSE state(no preroll state change).
2723                         that's why set position through property. */
2724                 if ((MMPLAYER_IS_RTSP_STREAMING(player)) &&
2725                         (MMPLAYER_CURRENT_STATE(player) == MM_PLAYER_STATE_PAUSED) &&
2726                         (MMPLAYER_PREV_STATE(player) == MM_PLAYER_STATE_READY) &&
2727                         (!player->videodec_linked) && (!player->audiodec_linked)) {
2728
2729                         g_object_set(player->pipeline->mainbin[MMPLAYER_M_SRC].gst, "pending-start-position", pos_nsec, NULL);
2730                         LOGD("[%s] set position =%"GST_TIME_FORMAT,
2731                                         GST_ELEMENT_NAME(player->pipeline->mainbin[MMPLAYER_M_SRC].gst), GST_TIME_ARGS(pos_nsec));
2732                         player->seek_state = MMPLAYER_SEEK_NONE;
2733                         MMPLAYER_POST_MSG(player, MM_MESSAGE_SEEK_COMPLETED, NULL);
2734                 } else {
2735                         ret = __mmplayer_gst_seek(player, player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, player->playback_rate,
2736                                                         GST_FORMAT_TIME, seek_flags,
2737                                                         GST_SEEK_TYPE_SET, pos_nsec, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
2738                 }
2739
2740                 if (!ret) {
2741                         LOGE("failed to set position.");
2742                         goto SEEK_ERROR;
2743                 }
2744         }
2745         break;
2746
2747         case MM_PLAYER_POS_FORMAT_PERCENT:
2748         {
2749                 LOGD("seeking to %"G_GINT64_FORMAT"%%", position);
2750
2751                 if (player->seek_state != MMPLAYER_SEEK_NONE) {
2752                         LOGD("not completed seek");
2753                         return MM_ERROR_PLAYER_DOING_SEEK;
2754                 }
2755
2756                 if (!internal_called)
2757                         player->seek_state = MMPLAYER_SEEK_IN_PROGRESS;
2758
2759                 /* FIXIT : why don't we use 'GST_FORMAT_PERCENT' */
2760                 pos_nsec = (gint64)((position * player->duration) / 100);
2761                 ret = __mmplayer_gst_seek(player, player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, player->playback_rate,
2762                                                 GST_FORMAT_TIME, seek_flags,
2763                                                 GST_SEEK_TYPE_SET, pos_nsec, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
2764                 if (!ret) {
2765                         LOGE("failed to set position. pos[%"G_GINT64_FORMAT"] dur[%"G_GINT64_FORMAT"] ", pos_nsec, player->duration);
2766                         goto SEEK_ERROR;
2767                 }
2768         }
2769         break;
2770
2771         default:
2772                 goto INVALID_ARGS;
2773         }
2774
2775         /* NOTE : store last seeking point to overcome some bad operation
2776           *     (returning zero when getting current position) of some elements
2777           */
2778         player->last_position = pos_nsec;
2779
2780         /* MSL should guarante playback rate when seek is selected during trick play of fast forward. */
2781         if (player->playback_rate > 1.0)
2782                 _mmplayer_set_playspeed((MMHandleType)player, player->playback_rate, FALSE);
2783
2784         if ((!internal_called) &&
2785             (player->streamer) && (player->streamer->buffering_state & MM_PLAYER_BUFFERING_IN_PROGRESS)) {
2786                 LOGD("buffering should be reset after seeking");
2787                 player->streamer->buffering_state = MM_PLAYER_BUFFERING_ABORT;
2788                 player->streamer->buffering_percent = 100; /* after seeking, new per can be non-zero. */
2789         }
2790
2791         MMPLAYER_FLEAVE();
2792         return MM_ERROR_NONE;
2793
2794 PENDING:
2795         player->pending_seek.is_pending = TRUE;
2796         player->pending_seek.format = format;
2797         player->pending_seek.pos = position;
2798
2799         LOGW("player current-state : %s, pending-state : %s, just preserve pending position(%"G_GINT64_FORMAT").\n",
2800                 MMPLAYER_STATE_GET_NAME(MMPLAYER_CURRENT_STATE(player)),
2801                 MMPLAYER_STATE_GET_NAME(MMPLAYER_PENDING_STATE(player)),
2802                 player->pending_seek.pos);
2803
2804         return MM_ERROR_NONE;
2805
2806 INVALID_ARGS:
2807         LOGE("invalid arguments, position: %"G_GINT64_FORMAT" dur : %"G_GINT64_FORMAT" format : %d \n", position, player->duration, format);
2808         return MM_ERROR_INVALID_ARGUMENT;
2809
2810 SEEK_ERROR:
2811         player->seek_state = MMPLAYER_SEEK_NONE;
2812         return MM_ERROR_PLAYER_SEEK;
2813 }
2814
2815 int
2816 __mmplayer_gst_get_position(mm_player_t* player, int format, gint64* position)
2817 {
2818 #define TRICKPLAY_OFFSET GST_MSECOND
2819
2820         MMPlayerStateType current_state = MM_PLAYER_STATE_NONE;
2821         gint64 pos_nsec = 0;
2822         gboolean ret = TRUE;
2823
2824         MMPLAYER_RETURN_VAL_IF_FAIL(player && position && player->pipeline && player->pipeline->mainbin,
2825                 MM_ERROR_PLAYER_NOT_INITIALIZED);
2826
2827         current_state = MMPLAYER_CURRENT_STATE(player);
2828
2829         /* NOTE : query position except paused state to overcome some bad operation
2830          * please refer to below comments in details
2831          */
2832         if (current_state != MM_PLAYER_STATE_PAUSED)
2833                 ret = gst_element_query_position(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, GST_FORMAT_TIME, &pos_nsec);
2834
2835         /* NOTE : get last point to overcome some bad operation of some elements
2836          *(returning zero when getting current position in paused state
2837          * and when failed to get postion during seeking
2838          */
2839         if ((current_state == MM_PLAYER_STATE_PAUSED) || (!ret)) {
2840                 LOGD("pos_nsec = %"GST_TIME_FORMAT" and ret = %d and state = %d", GST_TIME_ARGS(pos_nsec), ret, current_state);
2841
2842                 if (player->playback_rate < 0.0)
2843                         pos_nsec = player->last_position - TRICKPLAY_OFFSET;
2844                 else
2845                         pos_nsec = player->last_position;
2846
2847                 if (!ret)
2848                         pos_nsec = player->last_position;
2849                 else
2850                         player->last_position = pos_nsec;
2851
2852                 LOGD("returning last point : %"GST_TIME_FORMAT, GST_TIME_ARGS(pos_nsec));
2853
2854         } else {
2855                 if (player->duration > 0 && pos_nsec > player->duration)
2856                         pos_nsec = player->duration;
2857
2858                 player->last_position = pos_nsec;
2859         }
2860
2861         switch (format) {
2862         case MM_PLAYER_POS_FORMAT_TIME:
2863                 *position = pos_nsec;
2864                 break;
2865
2866         case MM_PLAYER_POS_FORMAT_PERCENT:
2867         {
2868                 if (player->duration <= 0) {
2869                         LOGD("duration is [%"G_GINT64_FORMAT"], so returning position 0\n", player->duration);
2870                         *position = 0;
2871                 } else {
2872                         LOGD("position is [%"G_GINT64_FORMAT"] nsec , duration is [%"G_GINT64_FORMAT"] nsec", pos_nsec, player->duration);
2873                         *position = (gint64)(pos_nsec * 100 / player->duration);
2874                 }
2875                 break;
2876         }
2877         default:
2878                 return MM_ERROR_PLAYER_INTERNAL;
2879         }
2880
2881         return MM_ERROR_NONE;
2882 }
2883
2884 int __mmplayer_gst_get_buffer_position(mm_player_t* player, int format, unsigned long* start_pos, unsigned long* stop_pos)
2885 {
2886 #define STREAMING_IS_FINISHED   0
2887 #define BUFFERING_MAX_PER       100
2888 #define DEFAULT_PER_VALUE       -1
2889 #define CHECK_PERCENT_VALUE(a, min, max)(((a) > (min)) ? (((a) < (max)) ? (a) : (max)) : (min))
2890
2891         MMPlayerGstElement *mainbin = NULL;
2892         gint start_per = DEFAULT_PER_VALUE, stop_per = DEFAULT_PER_VALUE;
2893         gint64 buffered_total = 0;
2894         gint64 position = 0;
2895         gint buffered_sec = -1;
2896         GstBufferingMode mode = GST_BUFFERING_STREAM;
2897         gint64 content_size_time = player->duration;
2898         guint64 content_size_bytes = player->http_content_size;
2899
2900         MMPLAYER_RETURN_VAL_IF_FAIL(player &&
2901                                                 player->pipeline &&
2902                                                 player->pipeline->mainbin,
2903                                                 MM_ERROR_PLAYER_NOT_INITIALIZED);
2904
2905         MMPLAYER_RETURN_VAL_IF_FAIL(start_pos && stop_pos, MM_ERROR_INVALID_ARGUMENT);
2906
2907         *start_pos = 0;
2908         *stop_pos = 0;
2909
2910         if (!MMPLAYER_IS_HTTP_STREAMING(player)) {
2911                 /* and rtsp is not ready yet. */
2912                 LOGW("it's only used for http streaming case.\n");
2913                 return MM_ERROR_PLAYER_NO_OP;
2914         }
2915
2916         if (format != MM_PLAYER_POS_FORMAT_PERCENT) {
2917                 LOGW("Time format is not supported yet.\n");
2918                 return MM_ERROR_INVALID_ARGUMENT;
2919         }
2920
2921         if (content_size_time <= 0 || content_size_bytes <= 0) {
2922                 LOGW("there is no content size.");
2923                 return MM_ERROR_NONE;
2924         }
2925
2926         if (__mmplayer_gst_get_position(player, MM_PLAYER_POS_FORMAT_TIME, &position) != MM_ERROR_NONE) {
2927                 LOGW("fail to get current position.");
2928                 return MM_ERROR_NONE;
2929         }
2930
2931         LOGD("pos %"G_GINT64_FORMAT" msec, dur %d sec, len %"G_GUINT64_FORMAT" bytes",
2932                 GST_TIME_AS_MSECONDS(position), (guint)GST_TIME_AS_SECONDS(content_size_time), content_size_bytes);
2933
2934         mainbin = player->pipeline->mainbin;
2935         start_per = (gint)(floor(100 *(gdouble)position / (gdouble)content_size_time));
2936
2937         if (mainbin[MMPLAYER_M_MUXED_S_BUFFER].gst) {
2938                 GstQuery *query = NULL;
2939                 gint byte_in_rate = 0, byte_out_rate = 0;
2940                 gint64 estimated_total = 0;
2941
2942                 query = gst_query_new_buffering(GST_FORMAT_BYTES);
2943                 if (!query || !gst_element_query(mainbin[MMPLAYER_M_MUXED_S_BUFFER].gst, query)) {
2944                         LOGW("fail to get buffering query from queue2");
2945                         if (query)
2946                                 gst_query_unref(query);
2947                         return MM_ERROR_NONE;
2948                 }
2949
2950                 gst_query_parse_buffering_stats(query, &mode, &byte_in_rate, &byte_out_rate, NULL);
2951                 LOGD("mode %d, in_rate %d, out_rate %d", mode, byte_in_rate, byte_out_rate);
2952
2953                 if (mode == GST_BUFFERING_STREAM) {
2954                         /* using only queue in case of push mode(ts / mp3) */
2955                         if (gst_element_query_position(mainbin[MMPLAYER_M_SRC].gst,
2956                                 GST_FORMAT_BYTES, &buffered_total)) {
2957                                 LOGD("buffered_total %"G_GINT64_FORMAT, buffered_total);
2958                                 stop_per = 100 * buffered_total / content_size_bytes;
2959                         }
2960                 } else {
2961                         /* GST_BUFFERING_TIMESHIFT or GST_BUFFERING_DOWNLOAD */
2962                         guint idx = 0;
2963                         guint num_of_ranges = 0;
2964                         gint64 start_byte = 0, stop_byte = 0;
2965
2966                         gst_query_parse_buffering_range(query, NULL, NULL, NULL, &estimated_total);
2967                         if (estimated_total != STREAMING_IS_FINISHED) {
2968                                 /* buffered size info from queue2 */
2969                                 num_of_ranges = gst_query_get_n_buffering_ranges(query);
2970                                 for (idx = 0; idx < num_of_ranges; idx++) {
2971                                         gst_query_parse_nth_buffering_range(query, idx, &start_byte, &stop_byte);
2972                                         LOGD("range %d, %"G_GINT64_FORMAT" ~ %"G_GUINT64_FORMAT, idx, start_byte, stop_byte);
2973
2974                                         buffered_total += (stop_byte - start_byte);
2975                                 }
2976                         } else
2977                                 stop_per = BUFFERING_MAX_PER;
2978                 }
2979                 gst_query_unref(query);
2980         }
2981
2982         if (stop_per == DEFAULT_PER_VALUE) {
2983                 guint dur_sec = (guint)(content_size_time/GST_SECOND);
2984                 if (dur_sec > 0) {
2985                         guint avg_byterate = (guint)(content_size_bytes/dur_sec);
2986
2987                         /* buffered size info from multiqueue */
2988                         if (mainbin[MMPLAYER_M_DEMUXED_S_BUFFER].gst) {
2989                                 guint curr_size_bytes = 0;
2990                                 g_object_get(G_OBJECT(mainbin[MMPLAYER_M_DEMUXED_S_BUFFER].gst),
2991                                         "curr-size-bytes", &curr_size_bytes, NULL);
2992                                 LOGD("curr_size_bytes of multiqueue = %d", curr_size_bytes);
2993                                 buffered_total += curr_size_bytes;
2994                         }
2995
2996                         if (avg_byterate > 0)
2997                                 buffered_sec = (gint)(ceil((gdouble)buffered_total/(gdouble)avg_byterate));
2998                         else if (player->total_maximum_bitrate > 0)
2999                                 buffered_sec = (gint)(ceil((gdouble)GET_BIT_FROM_BYTE(buffered_total)/(gdouble)player->total_maximum_bitrate));
3000                         else if (player->total_bitrate > 0)
3001                                 buffered_sec = (gint)(ceil((gdouble)GET_BIT_FROM_BYTE(buffered_total)/(gdouble)player->total_bitrate));
3002
3003                         if (buffered_sec >= 0)
3004                                 stop_per = start_per +(gint)(ceil)(100*(gdouble)buffered_sec/(gdouble)dur_sec);
3005                 }
3006         }
3007
3008         *start_pos = CHECK_PERCENT_VALUE(start_per, 0, 100);
3009         *stop_pos = CHECK_PERCENT_VALUE(stop_per, *start_pos, 100);
3010
3011         LOGD("buffered info: %"G_GINT64_FORMAT" bytes, %d sec, per %lu~%lu\n",
3012                 buffered_total, buffered_sec, *start_pos, *stop_pos);
3013
3014         return MM_ERROR_NONE;
3015 }
3016