Small clean of subtitles applet code.
[mafwsubrenderer] / qmafw-gst-subtitles-renderer / src / mafw-gst-renderer-worker.c
1 /*
2  * This file is a part of MAFW
3  *
4  * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved.
5  *
6  * Contact: Visa Smolander <visa.smolander@nokia.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; version 2.1 of
11  * the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21  * 02110-1301 USA
22  *
23  */
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include <string.h>
29 #include <glib.h>
30 #include <glib/gstdio.h>
31 #include <unistd.h>
32 #include <X11/Xlib.h>
33 #include <gst/interfaces/xoverlay.h>
34 #include <gst/pbutils/missing-plugins.h>
35 #include <gst/base/gstbasesink.h>
36 #include <context_provider.h>
37
38 #include "mafw-gst-renderer-worker.h"
39 #include "mafw-gst-renderer-utils.h"
40
41 #define UNUSED(x) (void)(x)
42
43 /* context provider DBus name must be the same as the .context file name without
44  * the .context suffix, service name in the .context file must be the same too */
45 #define CONTEXT_PROVIDER_BUS_NAME       "com.nokia.mafw.context_provider.libqmafw_gst_renderer"
46 #define CONTEXT_PROVIDER_KEY_NOWPLAYING "Media.NowPlaying"
47 #define CONTEXT_PROVIDER_KEY_NOWPLAYING_TITLE    "title"
48 #define CONTEXT_PROVIDER_KEY_NOWPLAYING_ALBUM    "album"
49 #define CONTEXT_PROVIDER_KEY_NOWPLAYING_ARTIST   "artist"
50 #define CONTEXT_PROVIDER_KEY_NOWPLAYING_GENRE    "genre"
51 #define CONTEXT_PROVIDER_KEY_NOWPLAYING_RESOURCE "resource"
52 #define CONTEXT_PROVIDER_KEY_NOWPLAYING_DURATION "duration"
53
54 #define WORKER_ERROR g_quark_from_static_string("com.nokia.mafw.error.renderer")
55
56 #define MAFW_GST_RENDERER_WORKER_DURATION_AND_SEEKABILITY_LAZY_TIMEOUT 4000
57 #define MAFW_GST_RENDERER_WORKER_DURATION_AND_SEEKABILITY_FAST_TIMEOUT 200
58 #define MAFW_GST_RENDERER_WORKER_DURATION_AND_SEEKABILITY_LOOP_LIMIT 10
59 #define MAFW_GST_MISSING_TYPE_DECODER "decoder"
60 #define MAFW_GST_MISSING_TYPE_ENCODER "encoder"
61
62 #define MAFW_TMP_URI_LEN 2048
63
64 #define STREAM_TYPE_MMS "mms://"
65 #define STREAM_TYPE_MMSH "mmsh://"
66 #define MAFW_GST_MMSH_CONNECTION_SPEED "2000"       /* kbit/s */
67 #define MAFW_GST_MMSH_TCP_TIMEOUT "30000000"        /* microseconds */
68
69 /* struct needed when emitting renderer art/frames as image files */
70 typedef struct {
71     MafwGstRendererWorker *worker;
72     gint metadata_key;
73     const gchar *filename;
74 } SaveGraphicData;
75
76 /* Forward declarations. */
77 static void _do_play(MafwGstRendererWorker *worker);
78
79 static void _do_seek(MafwGstRendererWorker *worker,
80                      GstSeekType seek_type,
81                      gint position,
82                      gboolean key_frame_seek,
83                      GError **error);
84
85 static gboolean _set_value(GValue *v, GType type, gconstpointer value);
86
87 static void _emit_metadatas(MafwGstRendererWorker *worker);
88
89 static gboolean _current_metadata_add(MafwGstRendererWorker *worker,
90                                       const gint key,
91                                       GType type,
92                                       const gpointer value);
93
94 static gpointer _set_context_map_value(gpointer map,
95                                        const gchar *tag,
96                                        const gchar *value);
97
98 /*
99  * Is used to prevent a critical log from context fw in case of multiple initialisations.
100  * Common to all renderers in the process.
101  */
102 static gboolean _context_fw_initialised = FALSE;
103
104 /*
105  * Sends @error to MafwGstRenderer.  Only call this from the glib main thread,
106  * or face the consequences.  @err is free'd.
107  */
108 static void _send_error(MafwGstRendererWorker *worker, GError *err)
109 {
110     worker->is_error = TRUE;
111     if (worker->notify_error_handler)
112     {
113         /* remap a possible gst ecode to worker ecode */
114         err->code = remap_gst_error_code(err);
115         worker->notify_error_handler(worker, worker->owner, err);
116     }
117     g_error_free(err);
118 }
119
120 configuration* _create_default_configuration()
121 {
122     configuration *config = g_malloc0(sizeof(configuration));
123     config->asink = g_strdup("pulsesink");
124     config->vsink = g_strdup("omapxvsink");
125     config->flags = 71;
126     config->buffer_time = 600000; /* microseconds */
127     config->latency_time = 100000; /* microseconds */
128     config->autoload_subtitles = TRUE;
129     config->subtitle_encoding = NULL;
130     config->subtitle_font = g_strdup("Sans Bold 18");
131
132     /* timers */
133     config->milliseconds_to_pause_frame = 700; /* milliseconds */
134     config->seconds_to_pause_to_ready = 3; /* seconds */
135
136     /* dhmmixer */
137     config->use_dhmmixer = TRUE;
138
139     config->mobile_surround_music.state = 0;
140     config->mobile_surround_music.room = 2;
141     config->mobile_surround_music.color = 2;
142     config->mobile_surround_video.state = 0;
143     config->mobile_surround_video.room = 2;
144     config->mobile_surround_video.color = 2;
145
146     return config;
147 }
148
149 void _free_configuration(configuration* config)
150 {
151     g_free(config->asink);
152     g_free(config->vsink);
153
154     g_free(config);
155 }
156
157 /*
158  * Posts an @error on the gst bus.  _async_bus_handler will then pick it up and
159  * forward to MafwGstRenderer.  @err is free'd.
160  */
161 static void _post_error(MafwGstRendererWorker *worker, GError *err)
162 {
163     gst_bus_post(worker->bus,
164                  gst_message_new_error(GST_OBJECT(worker->pipeline),
165                                        err,
166                                        NULL));
167     g_error_free(err);
168 }
169
170 static gboolean _set_value(GValue *v, GType type, gconstpointer value)
171 {
172
173     gboolean ret = TRUE;
174
175     if (v && value)
176     {
177         memset(v, 0, sizeof(GValue));
178         g_value_init(v, type);
179
180         if (type == G_TYPE_STRING) {
181             g_value_set_string(v, (const gchar*)value);
182         }
183         else if (type == G_TYPE_INT) {
184             g_value_set_int(v, *(gint*)value);
185         }
186         else if (type == G_TYPE_UINT) {
187             g_value_set_uint(v, *(uint*)value);
188         }
189         else if (type == G_TYPE_DOUBLE) {
190             g_value_set_double(v, *(gdouble*)value);
191         }
192         else if (type == G_TYPE_BOOLEAN) {
193             g_value_set_boolean(v, *(gboolean*)value);
194         }
195         else if (type == G_TYPE_INT64) {
196             g_value_set_int64(v, *(gint64*)value);
197         }
198         else if (type == G_TYPE_FLOAT) {
199             g_value_set_float(v, *(gfloat*)value);
200         }
201         else if (type == G_TYPE_VALUE_ARRAY) {
202             g_value_copy((GValue*)value,v);
203         }
204         else {
205             g_warning("%s: unknown g_type", G_STRFUNC);
206             ret = FALSE;
207         }
208     }
209     else
210     {
211         ret = FALSE;
212     }
213
214     return ret;
215
216 }
217
218 static void _emit_metadata(MafwGstRendererWorker *worker,
219                            gint metadata_key,
220                            GType type,
221                            gconstpointer value)
222 {
223
224     GValue v;
225
226     if (worker && worker->notify_metadata_handler &&
227         _set_value(&v, type, value))
228     {
229         GValueArray *array = g_value_array_new(0);
230         g_value_array_append(array, &v);
231         worker->notify_metadata_handler(worker,
232                                         worker->owner,
233                                         metadata_key,
234                                         G_TYPE_VALUE_ARRAY,
235                                         array);
236         g_value_array_free(array);
237         g_value_unset(&v);
238     }
239
240 }
241
242 static void _emit_property(MafwGstRendererWorker *worker,
243                            gint property,
244                            GType type,
245                            gconstpointer value)
246 {
247
248     GValue v;
249
250     if (worker && worker->notify_property_handler &&
251         _set_value(&v, type, value))
252     {
253         worker->notify_property_handler(worker, worker->owner, property, &v);
254         g_value_unset(&v);
255     }
256
257 }
258
259 static gchar *_init_tmp_file(void)
260 {
261     gint fd;
262     gchar *path = NULL;
263
264     fd = g_file_open_tmp("mafw-gst-renderer-XXXXXX.picture", &path, NULL);
265     if (fd >= 0 )
266     {
267         close(fd);
268     }
269
270     return path;
271 }
272
273 static void _destroy_tmp_file(MafwGstRendererWorker *worker, guint index)
274 {
275     g_unlink(worker->tmp_files_pool[index]);
276     g_free(worker->tmp_files_pool[index]);
277     worker->tmp_files_pool[index] = NULL;
278 }
279
280 static void _init_tmp_files_pool(MafwGstRendererWorker *worker)
281 {
282     guint8 i;
283
284     worker->tmp_files_pool_index = 0;
285
286     for (i = 0; i < MAFW_GST_RENDERER_MAX_TMP_FILES; i++) {
287         worker->tmp_files_pool[i] = NULL;
288     }
289 }
290
291 static void _destroy_tmp_files_pool(MafwGstRendererWorker *worker)
292 {
293     guint8 i;
294
295     for (i = 0; (i < MAFW_GST_RENDERER_MAX_TMP_FILES) &&
296          (worker->tmp_files_pool[i] != NULL); i++) {
297         g_unlink(worker->tmp_files_pool[i]);
298         g_free(worker->tmp_files_pool[i]);
299     }
300 }
301
302 static const gchar *_get_tmp_file_from_pool(MafwGstRendererWorker *worker)
303 {
304     gchar *path = worker->tmp_files_pool[worker->tmp_files_pool_index];
305
306     if (path == NULL) {
307         path = _init_tmp_file();
308         worker->tmp_files_pool[worker->tmp_files_pool_index] = path;
309     }
310     else
311     {
312         _destroy_tmp_file(worker, worker->tmp_files_pool_index);
313         path = _init_tmp_file();
314         worker->tmp_files_pool[worker->tmp_files_pool_index] = path;
315     }
316
317     if (++(worker->tmp_files_pool_index) >= MAFW_GST_RENDERER_MAX_TMP_FILES) {
318         worker->tmp_files_pool_index = 0;
319     }
320
321     return path;
322 }
323
324 static void _emit_gst_buffer_as_graphic_file_cb(GError *error,
325                                                 gpointer user_data)
326 {
327     SaveGraphicData *sgd = user_data;
328
329     if (error == NULL) {
330         /* Add the info to the current metadata. */
331         _current_metadata_add(sgd->worker,
332                               sgd->metadata_key,
333                               G_TYPE_STRING,
334                               (const gpointer)sgd->filename);
335
336         /* Emit the metadata. */
337         _emit_metadata(sgd->worker,
338                        sgd->metadata_key,
339                        G_TYPE_STRING,
340                        sgd->filename);
341     }
342     else
343     {
344         g_warning("could not emit graphic file: %s", error->message);
345     }
346
347     g_free(sgd);
348 }
349
350 static void _emit_gst_buffer_as_graphic_file(MafwGstRendererWorker *worker,
351                                              GstBuffer *buffer,
352                                              const gint metadata_key)
353 {
354     GstStructure *structure;
355     const gchar *mime = NULL;
356     GError *error = NULL;
357     SaveGraphicData *sgd;
358
359     g_return_if_fail((buffer != NULL) && GST_IS_BUFFER(buffer));
360
361     structure = gst_caps_get_structure(GST_BUFFER_CAPS(buffer), 0);
362     mime = gst_structure_get_name(structure);
363
364     /* video pause frame related branch */
365     if (g_str_has_prefix(mime, "video/x-raw")) {
366         const gchar *filename = _get_tmp_file_from_pool(worker);
367
368         if(worker->taking_screenshot)
369         {
370             worker->screenshot_handler(worker, worker->owner, NULL, NULL, TRUE);
371         }
372         worker->taking_screenshot = TRUE;
373         worker->screenshot_handler(worker, worker->owner, buffer, filename, FALSE);
374
375     /* gst image tag related branch */
376     } else if (g_str_has_prefix(mime, "image/")) {
377
378         sgd = g_new0(SaveGraphicData, 1);
379         sgd->worker = worker;
380         sgd->metadata_key = metadata_key;
381         sgd->filename = _get_tmp_file_from_pool(worker);
382
383         g_debug("dumping gst image %s directly to a file", mime);
384         g_file_set_contents(sgd->filename,
385                             (const gchar*)GST_BUFFER_DATA(buffer),
386                             GST_BUFFER_SIZE(buffer),
387                             &error);
388         _emit_gst_buffer_as_graphic_file_cb(error, sgd);
389         if (error) {
390             g_error_free(error);
391         }
392     } else {
393         g_warning("Mime type not supported, will not create a thumbnail");
394         gst_buffer_unref(buffer);
395     }
396 }
397
398 static gboolean _go_to_gst_ready(gpointer user_data)
399 {
400     g_debug("_go_to_gst_ready");
401     MafwGstRendererWorker *worker = user_data;
402
403     g_return_val_if_fail(worker->state == GST_STATE_PAUSED ||
404                          worker->prerolling, FALSE);
405
406     worker->seek_position = mafw_gst_renderer_worker_get_position(worker);
407
408     g_debug("going to GST_STATE_READY");
409     gst_element_set_state(worker->pipeline, GST_STATE_READY);
410     worker->in_ready = TRUE;
411     return FALSE;
412 }
413
414 static void _add_ready_timeout(MafwGstRendererWorker *worker)
415 {
416     if( worker->ready_timeout == 0 )
417     {
418         g_debug("Adding timeout to go to GST_STATE_READY");
419         worker->ready_timeout =
420                 g_timeout_add_seconds(
421                     worker->config->seconds_to_pause_to_ready,
422                     _go_to_gst_ready,
423                     worker);
424     }
425 }
426
427 static void _remove_ready_timeout(MafwGstRendererWorker *worker)
428 {
429     if( worker->ready_timeout != 0 )
430     {
431         g_debug("removing timeout for READY");
432         g_source_remove(worker->ready_timeout);
433         worker->ready_timeout = 0;
434     }
435 }
436
437 static gboolean _take_pause_frame(gpointer user_data)
438 {
439     MafwGstRendererWorker *worker = user_data;
440
441     if( worker->pause_frame_taken && worker->pause_frame_buffer )
442     {
443         gst_buffer_unref(worker->pause_frame_buffer);
444         worker->pause_frame_buffer = NULL;
445         return FALSE;
446     }
447
448     if (worker->pause_frame_buffer != NULL) {
449         worker->pause_frame_taken = TRUE;
450         _emit_gst_buffer_as_graphic_file(
451             worker,
452             worker->pause_frame_buffer,
453             WORKER_METADATA_KEY_PAUSED_THUMBNAIL_URI);
454         worker->pause_frame_buffer = NULL;
455     }
456     return FALSE;
457 }
458
459 static void _add_pause_frame_timeout(MafwGstRendererWorker *worker)
460 {
461     if (worker->media.has_visual_content && worker->current_frame_on_pause && worker->seek_position == -1)
462     {            
463         if (!worker->pause_frame_timeout)
464         {
465             GstBuffer *buffer = NULL;
466             g_object_get(worker->pipeline, "frame", &buffer, NULL);
467             if( buffer )
468             {
469                 GstBuffer *copy = gst_buffer_copy(buffer);
470                 gst_buffer_copy_metadata(copy, buffer, GST_BUFFER_COPY_ALL);
471                 worker->pause_frame_buffer = copy;
472                 gst_buffer_unref(buffer);
473
474                 g_debug("Adding timeout to go to current frame capture");
475                 worker->pause_frame_timeout =
476                         g_timeout_add_full(
477                                 G_PRIORITY_DEFAULT,
478                                 worker->config->milliseconds_to_pause_frame,
479                                 _take_pause_frame,
480                                 worker, NULL);
481             }
482             else
483             {
484                 g_warning("MafwGstRenderer Worker: Could not get buffer from pipeline! Maybe at EOS?");
485             }
486         }
487     } else {
488         g_debug("Not adding timeout to take pause frame.");
489         worker->pause_frame_timeout = 0;
490     }
491 }
492
493 static void _remove_pause_frame_timeout(MafwGstRendererWorker *worker)
494 {
495     if (worker->pause_frame_timeout != 0) {
496         g_debug("removing timeout for pause frame!");
497         g_source_remove(worker->pause_frame_timeout);
498         worker->pause_frame_timeout = 0;
499     }
500
501     if(worker->taking_screenshot)
502     {
503         worker->screenshot_handler(worker, worker->owner, NULL, NULL, TRUE);
504         worker->taking_screenshot = FALSE;
505     }
506     else
507     {
508         /* in this case the buffer has not been given to the
509          * screenshot component to be processed */
510         if(worker->pause_frame_buffer)
511         {
512             gst_buffer_unref(worker->pause_frame_buffer);
513             worker->pause_frame_buffer = NULL;
514         }
515     }
516 }
517
518 static gboolean _emit_video_info(MafwGstRendererWorker *worker)
519 {
520
521     _emit_metadata(worker,
522                    WORKER_METADATA_KEY_RES_X,
523                    G_TYPE_INT,
524                    &worker->media.video_width);
525
526     _emit_metadata(worker,
527                    WORKER_METADATA_KEY_RES_Y,
528                    G_TYPE_INT,
529                    &worker->media.video_height);
530
531     _emit_metadata(worker,
532                    WORKER_METADATA_KEY_VIDEO_FRAMERATE,
533                    G_TYPE_DOUBLE,
534                    &worker->media.fps);
535
536     return FALSE;
537
538 }
539
540 /*
541  * Checks if the video details are supported.  It also extracts other useful
542  * information (such as PAR and framerate) from the caps, if available.  NOTE:
543  * this will be called from a different thread than glib's mainloop (when
544  * invoked via _stream_info_cb);  don't call MafwGstRenderer directly.
545  *
546  * Returns: TRUE if video details are acceptable.
547  */
548 static gboolean _handle_video_info(MafwGstRendererWorker *worker,
549                                    const GstStructure *structure)
550 {
551     gint width, height;
552     gdouble fps;
553
554     width = height = 0;
555     gst_structure_get_int(structure, "width", &width);
556     gst_structure_get_int(structure, "height", &height);
557     g_debug("video size: %d x %d", width, height);
558     if (gst_structure_has_field(structure, "pixel-aspect-ratio"))
559     {
560         gst_structure_get_fraction(structure,
561                                    "pixel-aspect-ratio",
562                                    &worker->media.par_n,
563                                    &worker->media.par_d);
564         g_debug("video PAR: %d:%d", worker->media.par_n, worker->media.par_d);
565         width = width * worker->media.par_n / worker->media.par_d;
566     }
567
568     fps = 1.0;
569     if (gst_structure_has_field(structure, "framerate"))
570     {
571         gint fps_n, fps_d;
572
573         gst_structure_get_fraction(structure, "framerate", &fps_n, &fps_d);
574         if (fps_d > 0) {
575             fps = (gdouble)fps_n / (gdouble)fps_d;
576         }
577         g_debug("video fps: %f", fps);
578     }
579
580     worker->media.video_width = width;
581     worker->media.video_height = height;
582     worker->media.fps = fps;
583
584     _current_metadata_add(worker, WORKER_METADATA_KEY_RES_X, G_TYPE_INT,
585                           (const gpointer)&width);
586     _current_metadata_add(worker, WORKER_METADATA_KEY_RES_Y, G_TYPE_INT,
587                           (const gpointer)&height);
588     _current_metadata_add(worker, WORKER_METADATA_KEY_VIDEO_FRAMERATE,
589                           G_TYPE_DOUBLE, (const gpointer)&fps);
590
591     /* Emit the metadata.*/
592     g_idle_add((GSourceFunc)_emit_video_info, worker);
593     return TRUE;
594 }
595
596 static void _parse_stream_info_item(MafwGstRendererWorker *worker, GObject *obj)
597 {
598     GParamSpec *pspec;
599     GEnumValue *val;
600     gint type;
601
602     g_object_get(obj, "type", &type, NULL);
603     pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(obj), "type");
604     if(!pspec)
605         return;
606     val = g_enum_get_value(G_PARAM_SPEC_ENUM(pspec)->enum_class, type);
607     if (!val)
608         return;
609     if (!g_ascii_strcasecmp(val->value_nick, "video") ||
610         !g_ascii_strcasecmp(val->value_name, "video"))
611     {
612         GstCaps *vcaps;
613         GstObject *object;
614
615         object = NULL;
616         g_object_get(obj, "object", &object, NULL);
617         vcaps = NULL;
618         if (object) {
619             vcaps = gst_pad_get_caps(GST_PAD_CAST(object));
620         } else {
621             g_object_get(obj, "caps", &vcaps, NULL);
622             gst_caps_ref(vcaps);
623         }
624         if (vcaps) {
625             if (gst_caps_is_fixed(vcaps))
626             {
627                 _handle_video_info(worker, gst_caps_get_structure(vcaps, 0));
628             }
629             gst_caps_unref(vcaps);
630         }
631     }
632 }
633
634 /* It always returns FALSE, because it is used as an idle callback as well. */
635 static gboolean _parse_stream_info(MafwGstRendererWorker *worker)
636 {
637     GList *stream_info, *s;
638
639     stream_info = NULL;
640     if (g_object_class_find_property(G_OBJECT_GET_CLASS(worker->pipeline),
641                                      "stream-info"))
642     {
643         g_object_get(worker->pipeline, "stream-info", &stream_info, NULL);
644     }
645     for (s = stream_info; s; s = g_list_next(s))
646         _parse_stream_info_item(worker, G_OBJECT(s->data));
647     return FALSE;
648 }
649
650 static void mafw_gst_renderer_worker_apply_xid(MafwGstRendererWorker *worker)
651 {
652     /* Set sink to render on the provided XID if we have do have
653        a XID a valid video sink and we are rendering video content */
654     if (worker->xid &&
655         worker->vsink &&
656         worker->media.has_visual_content)
657     {
658         g_debug ("Setting overlay, window id: %x", (gint) worker->xid);
659         gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(worker->vsink), worker->xid);
660
661
662         /* Ask the gst to redraw the frame if we are paused */
663         /* TODO: in MTG this works only in non-fs -> fs way. */
664         if (worker->state == GST_STATE_PAUSED)
665         {
666             gst_x_overlay_expose(GST_X_OVERLAY(worker->vsink));
667         }
668     } else {
669         g_debug("Not setting overlay for window id: %x", (gint) worker->xid);
670     }
671 }
672
673 static void mafw_gst_renderer_worker_apply_render_rectangle(MafwGstRendererWorker *worker)
674 {
675     /* Set sink to render on the provided XID if we have do have
676        a XID a valid video sink and we are rendering video content */
677     if (worker->xid &&
678         worker->vsink &&
679         worker->media.has_visual_content
680         &&
681         (worker->x_overlay_rectangle.x >= 0 &&
682          worker->x_overlay_rectangle.y >= 0 &&
683          worker->x_overlay_rectangle.width >= 0 &&
684          worker->x_overlay_rectangle.height >= 0) )
685     {
686         g_debug("Applying render rectangle: X:%d,Y:%d  Width:%d, Height:%d",
687                 worker->x_overlay_rectangle.x,
688                 worker->x_overlay_rectangle.y,
689                 worker->x_overlay_rectangle.width,
690                 worker->x_overlay_rectangle.height);
691
692         gst_x_overlay_set_render_rectangle(GST_X_OVERLAY(worker->vsink),
693                                            worker->x_overlay_rectangle.x,
694                                            worker->x_overlay_rectangle.y,
695                                            worker->x_overlay_rectangle.width,
696                                            worker->x_overlay_rectangle.height);
697         /* Ask the gst to redraw the frame if we are paused */
698         /* TODO: in MTG this works only in non-fs -> fs way. */
699         if (worker->state == GST_STATE_PAUSED)
700         {
701             gst_x_overlay_expose(GST_X_OVERLAY(worker->vsink));
702         }
703
704     } else {
705         g_debug("Not setting render rectangle for window id: %x", (gint) worker->xid);
706     }
707 }
708
709 /*
710  * GstBus synchronous message handler.  NOTE that this handler is NOT invoked
711  * from the glib thread, so be careful what you do here.
712  */
713 static GstBusSyncReply _sync_bus_handler(GstBus *bus,
714                                          GstMessage *msg,
715                                          MafwGstRendererWorker *worker)
716 {
717
718     UNUSED(bus);
719
720     if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ELEMENT &&
721         gst_structure_has_name(msg->structure, "prepare-xwindow-id"))
722     {
723         g_debug("got prepare-xwindow-id");
724         worker->media.has_visual_content = TRUE;
725         set_dolby_video_property(worker, worker->config->mobile_surround_video.state);
726         set_dolby_video_sound_property(worker, worker->config->mobile_surround_video.room, TRUE);
727         set_dolby_video_sound_property(worker, worker->config->mobile_surround_video.color, FALSE);
728         /* The user has to preset the XID, we don't create windows by
729          * ourselves. */
730         if (!worker->xid || !worker->vsink) {
731             /* We must post an error message to the bus that will be picked up
732              * by _async_bus_handler.  Calling the notification function
733              * directly from here (different thread) is not healthy. */
734             g_warning("No video window or video-sink set!");
735             _post_error(worker,
736                         g_error_new_literal(WORKER_ERROR,
737                                             WORKER_ERROR_PLAYBACK,
738                                             "No video window XID or video-sink set"));
739             gst_message_unref (msg);
740             return GST_BUS_DROP;
741         } else {
742             g_debug ("Video window to use is: %x", (gint)worker->xid);
743         }
744
745         /* Instruct vsink to use the client-provided window */
746         mafw_gst_renderer_worker_apply_xid(worker);
747         /* Instruct vsink to use the required render rectangle */
748         mafw_gst_renderer_worker_apply_render_rectangle(worker);
749
750         /* Handle colorkey and autopaint */
751         mafw_gst_renderer_worker_set_autopaint(worker, worker->autopaint);
752         g_object_get(worker->vsink, "colorkey", &worker->colorkey, NULL);
753         /* Defer the signal emission to the thread running the mainloop. */
754         if (worker->colorkey != -1) {
755             gst_bus_post(worker->bus,
756                          gst_message_new_application(
757                              GST_OBJECT(worker->vsink),
758                              gst_structure_empty_new("ckey")));
759         }
760         gst_message_unref (msg);
761         return GST_BUS_DROP;
762     }
763     /* do not unref message when returning PASS */
764     return GST_BUS_PASS;
765 }
766
767 static void _free_taglist_item(GstMessage *msg, gpointer data)
768 {
769     UNUSED(data);
770
771     gst_message_unref(msg);
772 }
773
774 static void _free_taglist(MafwGstRendererWorker *worker)
775 {
776     if (worker->tag_list != NULL)
777     {
778         g_ptr_array_foreach(worker->tag_list, (GFunc)_free_taglist_item, NULL);
779         g_ptr_array_free(worker->tag_list, TRUE);
780         worker->tag_list = NULL;
781     }
782 }
783
784 static gboolean _seconds_duration_equal(gint64 duration1, gint64 duration2)
785 {
786     gint64 duration1_seconds, duration2_seconds;
787
788     duration1_seconds = duration1 / GST_SECOND;
789     duration2_seconds = duration2 / GST_SECOND;
790
791     return duration1_seconds == duration2_seconds;
792 }
793
794 static void _check_duration(MafwGstRendererWorker *worker, gint64 value)
795 {
796     gboolean right_query = TRUE;
797
798     if (value == -1) {
799         GstFormat format = GST_FORMAT_TIME;
800         right_query =
801             gst_element_query_duration(worker->pipeline, &format, &value);
802     }
803
804     if (right_query && value > 0
805             && !_seconds_duration_equal(worker->media.length_nanos, value))
806     {
807         gint64 duration = (value + (GST_SECOND/2)) / GST_SECOND;
808
809         /* Add the duration to the current metadata. */
810         if( _current_metadata_add(worker,
811                                   WORKER_METADATA_KEY_DURATION,
812                                   G_TYPE_INT64,
813                                   (const gpointer)&duration) )
814         {
815             _emit_metadata(worker,
816                            WORKER_METADATA_KEY_DURATION,
817                            G_TYPE_INT64,
818                            &duration);
819         }
820
821          /* Publish to context FW */
822         if( worker->context_nowplaying == NULL )
823         {
824             worker->context_nowplaying = context_provider_map_new();
825         }
826         context_provider_map_set_integer(worker->context_nowplaying,
827                                          CONTEXT_PROVIDER_KEY_NOWPLAYING_DURATION,
828                                          duration);
829         context_provider_set_map(CONTEXT_PROVIDER_KEY_NOWPLAYING,
830                                  worker->context_nowplaying, FALSE);
831          /* end of publishing to context FW */
832     }
833
834     if( right_query )
835     {
836         worker->media.length_nanos = value;
837     }
838
839     g_debug("media duration: %lld", worker->media.length_nanos);
840 }
841
842 static void _check_seekability(MafwGstRendererWorker *worker)
843 {
844     SeekabilityType seekable = SEEKABILITY_UNKNOWN;
845     if (worker->media.length_nanos >= 0 )
846     {
847         g_debug("Quering GStreamer for seekability");
848         GstQuery *seek_query;
849         GstFormat format = GST_FORMAT_TIME;
850         /* Query the seekability of the stream */
851         seek_query = gst_query_new_seeking(format);
852         if (gst_element_query(worker->pipeline, seek_query)) {
853             gboolean renderer_seekable = FALSE;
854             gst_query_parse_seeking(seek_query,
855                                     NULL,
856                                     &renderer_seekable,
857                                     NULL, NULL);
858             g_debug("GStreamer seekability %d", renderer_seekable);
859             seekable = renderer_seekable ? SEEKABILITY_SEEKABLE : SEEKABILITY_NO_SEEKABLE;
860         }
861         else
862         {
863             g_debug("Could not query pipeline for seekability! Using old value!");
864             seekable = worker->media.seekable;
865         }
866         gst_query_unref(seek_query);
867     }
868     else if( worker->media.length_nanos == DURATION_INDEFINITE )
869     {
870         /* duration indefinite, "clearly" not seekable */
871         seekable = SEEKABILITY_NO_SEEKABLE;
872     }
873     else
874     {
875         /* otherwise we'll use last known/guessed value */
876         seekable = worker->media.seekable;
877     }
878
879     g_debug("media seekable: %d", seekable);
880
881     /* If the seekability is unknown it is set as false and sent. After that it is
882        sent only if it changes to true
883        */
884     if( (seekable == SEEKABILITY_UNKNOWN && worker->media.seekable == SEEKABILITY_UNKNOWN)
885         || seekable != worker->media.seekable )
886     {
887         if( seekable != SEEKABILITY_NO_SEEKABLE )
888         {
889             worker->media.seekable = SEEKABILITY_SEEKABLE;
890         }
891         else
892         {
893             worker->media.seekable =  SEEKABILITY_NO_SEEKABLE;
894         }
895
896         gboolean is_seekable = (worker->media.seekable == SEEKABILITY_SEEKABLE);
897         _current_metadata_add(worker,
898                               WORKER_METADATA_KEY_IS_SEEKABLE,
899                               G_TYPE_BOOLEAN,
900                               (const gpointer)&is_seekable);
901         _emit_metadata(worker,
902                        WORKER_METADATA_KEY_IS_SEEKABLE,
903                        G_TYPE_BOOLEAN,
904                        &is_seekable);
905     }
906 }
907
908 static gboolean _query_duration_and_seekability_timeout(gpointer data)
909 {
910     MafwGstRendererWorker *worker = data;
911
912     if (!worker->in_ready)
913     {
914         _check_duration(worker, -1);
915         worker->duration_seek_timeout_loop_count += 1;
916
917         /* for worker's internal logic let's put the indefinite duration if loop limit has been reached */
918         /* this affects the seekability resolution */
919         if( worker->duration_seek_timeout_loop_count >= MAFW_GST_RENDERER_WORKER_DURATION_AND_SEEKABILITY_LOOP_LIMIT
920             && worker->media.length_nanos == DURATION_UNQUERIED )
921         {
922             worker->media.length_nanos = DURATION_INDEFINITE;
923         }
924
925         _check_seekability(worker);
926
927         if( worker->media.length_nanos >= DURATION_INDEFINITE )
928         {
929             worker->duration_seek_timeout = 0;
930             /* we've got a valid duration value no need to ask for more */
931             return FALSE;
932         }
933         else
934         {
935             return TRUE;
936         }
937     }
938     else
939     {
940         g_warning("_query_duration_and_seekability_timeout: We are in ready state, duration and seekability not checked.");
941         return FALSE;
942     }
943 }
944
945 /*
946  * Resets the media information.
947  */
948 static void _reset_media_info(MafwGstRendererWorker *worker)
949 {
950     if (worker->media.location) {
951         g_free(worker->media.location);
952         worker->media.location = NULL;
953     }
954     worker->media.length_nanos = DURATION_UNQUERIED;
955     worker->media.has_visual_content = FALSE;
956     worker->media.seekable = SEEKABILITY_UNKNOWN;
957     worker->media.video_width = 0;
958     worker->media.video_height = 0;
959     worker->media.fps = 0.0;
960 }
961
962 static void _reset_pipeline_and_worker(MafwGstRendererWorker *worker)
963 {
964
965     if (worker->pipeline) {
966         g_debug("destroying pipeline");
967         if (worker->async_bus_id) {
968             g_source_remove(worker->async_bus_id);
969             worker->async_bus_id = 0;
970         }
971         gst_element_set_state(worker->pipeline, GST_STATE_NULL);
972         if (worker->bus) {
973             gst_bus_set_sync_handler(worker->bus, NULL, NULL);
974             gst_object_unref(GST_OBJECT_CAST(worker->bus));
975             worker->bus = NULL;
976         }
977         gst_object_unref(worker->pipeline);
978         worker->pipeline = NULL;
979     }
980
981     worker->report_statechanges = TRUE;
982     worker->state = GST_STATE_NULL;
983     worker->prerolling = FALSE;
984     worker->is_live = FALSE;
985     worker->buffering = FALSE;
986     worker->is_stream = FALSE;
987     worker->is_error = FALSE;
988     worker->eos = FALSE;
989     worker->seek_position = -1;
990     worker->stay_paused = FALSE;
991     worker->playback_speed = 1;
992     worker->in_ready = FALSE;
993     _remove_ready_timeout(worker);
994     _remove_pause_frame_timeout(worker);
995     _free_taglist(worker);
996     if (worker->current_metadata) {
997         g_hash_table_destroy(worker->current_metadata);
998         worker->current_metadata = NULL;
999     }
1000
1001     if (worker->duration_seek_timeout != 0) {
1002         g_source_remove(worker->duration_seek_timeout);
1003         worker->duration_seek_timeout = 0;
1004     }
1005     worker->duration_seek_timeout_loop_count = 0;
1006
1007     _reset_media_info(worker);
1008
1009     /* removes all idle timeouts with this worker as data */
1010     while(g_idle_remove_by_data(worker));
1011 }
1012
1013
1014 /*
1015  * Called when the pipeline transitions into PAUSED state.  It extracts more
1016  * information from Gst.
1017  */
1018 static void _finalize_startup(MafwGstRendererWorker *worker)
1019 {
1020     /* Check video caps */
1021     if (worker->media.has_visual_content && worker->vsink) {
1022         GstPad *pad = GST_BASE_SINK_PAD(worker->vsink);
1023         GstCaps *caps = GST_PAD_CAPS(pad);
1024         if (caps && gst_caps_is_fixed(caps)) {
1025             GstStructure *structure;
1026             structure = gst_caps_get_structure(caps, 0);
1027             if (!_handle_video_info(worker, structure))
1028                 return;
1029         }
1030     }
1031
1032     /* Something might have gone wrong at this point already. */
1033     if (worker->is_error) {
1034         g_debug("Error occured during preroll");
1035         return;
1036     }
1037
1038     /* Streaminfo might reveal the media to be unsupported.  Therefore we
1039      * need to check the error again. */
1040     _parse_stream_info(worker);
1041     if (worker->is_error) {
1042         g_debug("Error occured. Leaving");
1043         return;
1044     }
1045
1046     /* Check duration and seekability */
1047     if (worker->duration_seek_timeout != 0) {
1048         g_source_remove(worker->duration_seek_timeout);
1049         worker->duration_seek_timeout = 0;
1050     }
1051
1052     _check_duration(worker, -1);
1053     _check_seekability(worker);
1054 }
1055
1056 static void _add_duration_seek_query_timeout(MafwGstRendererWorker *worker)
1057 {
1058     if(worker->duration_seek_timeout == 0)
1059     {
1060         gint timeout = 0;
1061         if( worker->duration_seek_timeout_loop_count >= MAFW_GST_RENDERER_WORKER_DURATION_AND_SEEKABILITY_LOOP_LIMIT
1062             || worker->media.length_nanos >= DURATION_INDEFINITE )
1063         {
1064             /* this is just for verifying the duration later on if it was received in PAUSED state early on */
1065             timeout = MAFW_GST_RENDERER_WORKER_DURATION_AND_SEEKABILITY_LAZY_TIMEOUT;
1066         }
1067         else
1068         {
1069             timeout = MAFW_GST_RENDERER_WORKER_DURATION_AND_SEEKABILITY_FAST_TIMEOUT;
1070         }
1071
1072         worker->duration_seek_timeout = g_timeout_add(
1073                 timeout,
1074                 _query_duration_and_seekability_timeout,
1075                 worker);
1076     }
1077 }
1078
1079 static void _do_pause_postprocessing(MafwGstRendererWorker *worker)
1080 {
1081     if (worker->notify_pause_handler) {
1082         worker->notify_pause_handler(worker, worker->owner);
1083     }
1084
1085     _add_pause_frame_timeout(worker);
1086     _add_ready_timeout(worker);
1087 }
1088
1089 static void _report_playing_state(MafwGstRendererWorker * worker)
1090 {
1091     if (worker->report_statechanges && worker->notify_play_handler)
1092     {
1093         worker->notify_play_handler( worker,
1094                                      worker->owner);
1095     }
1096 }
1097
1098 static void _handle_state_changed(GstMessage *msg,
1099                                   MafwGstRendererWorker *worker)
1100 {
1101     GstState newstate, oldstate;
1102     GstStateChange statetrans;
1103
1104     gst_message_parse_state_changed(msg, &oldstate, &newstate, NULL);
1105     statetrans = GST_STATE_TRANSITION(oldstate, newstate);
1106     g_debug("State changed: %d: %d -> %d", worker->state, oldstate, newstate);
1107
1108     /* If the state is the same we do nothing, otherwise, we keep it */
1109     if (worker->state == newstate)
1110     {
1111         /* This is used for saving correct pause frame after pauseAt.
1112          * If we doing normal seek we dont want to save pause frame.
1113          * We use gst_element_get_state to check if the state change is completed.
1114          * If gst_element_get_state returns GST_STATE_CHANGE_SUCCESS we know that
1115          * it's save to do pause_postprocessing */
1116         if (newstate == GST_STATE_PAUSED && worker->stay_paused &&
1117             gst_element_get_state(worker->pipeline, NULL, NULL, 0) == GST_STATE_CHANGE_SUCCESS)
1118         {
1119             worker->seek_position = mafw_gst_renderer_seeker_process(worker->seeker);
1120
1121             /* has seeking ended successfully? */
1122             if( worker->seek_position < 0 )
1123             {
1124                 /* we do pause_postprocessing for pauseAt */
1125                 _do_pause_postprocessing(worker);
1126             }
1127         }
1128
1129         /* the EOS flag should only be cleared if it has been set and seeking has been done
1130          * paused -> paused transition should only happen when seeking
1131          */
1132         if( newstate == GST_STATE_PAUSED && worker->eos )
1133         {
1134             worker->eos = FALSE;
1135         }
1136         return;
1137     }
1138
1139     worker->state = newstate;
1140
1141     switch (statetrans) {
1142         case GST_STATE_CHANGE_READY_TO_PAUSED:
1143             if (worker->in_ready) {
1144                 /* Woken up from READY, resume stream position and playback */
1145
1146                 /*live sources can be sought only in PLAYING state*/
1147                 if( !worker->is_live ) {
1148                     _do_seek(worker,
1149                              GST_SEEK_TYPE_SET,
1150                              worker->seek_position,
1151                              FALSE,
1152                              NULL);
1153                 }
1154
1155                 /* While buffering, we have to wait in PAUSED until we reach 100% before
1156                  * doing anything */
1157                 if (worker->buffering) {
1158                     return;
1159                 } else {
1160                     _do_play(worker);
1161                 }
1162             } else if (worker->prerolling && worker->report_statechanges && !worker->buffering) {
1163                 /* PAUSED after pipeline has been constructed. We check caps,
1164                  * seek and duration and if staying in pause is needed, we
1165                  * perform operations for pausing, such as current frame on
1166                  * pause and signalling state change and adding the timeout to
1167                  * go to ready */
1168                 g_debug ("Prerolling done, finalizaing startup");
1169                 _finalize_startup(worker);
1170
1171                 if (worker->stay_paused) {
1172                     /* then we can tell we're paused */
1173                     _do_pause_postprocessing(worker);
1174                 }
1175
1176                 if( worker->seek_position > 0 )
1177                 {
1178                     g_debug("Immediate seek from READY state to: %d", worker->seek_position);
1179                     _do_seek(worker, GST_SEEK_TYPE_SET,
1180                              worker->seek_position, FALSE, NULL);
1181
1182                     if(worker->vsink)
1183                     {
1184                         g_object_set(worker->vsink, "show-preroll-frame",
1185                                      TRUE, NULL);
1186                     }
1187
1188                     /* do_seek will set this to false, but we'll want to report state changes
1189                        when doing immediate seek from start */
1190                     worker->report_statechanges = TRUE;
1191                 }
1192                 worker->prerolling = FALSE;
1193                 _do_play(worker);
1194             }
1195             break;
1196         case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1197             /* When pausing we do the stuff, like signalling state, current
1198              * frame on pause and timeout to go to ready */
1199             if (worker->report_statechanges) {
1200                 _do_pause_postprocessing(worker);
1201             }
1202             break;
1203         case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1204
1205             /*live sources can be sought only in PLAYING state
1206               This seek should happen only after READY to PAUSED to PLAYING
1207               transitions
1208             */
1209             if( worker->report_statechanges
1210                     && worker->seek_position > -1
1211                     && worker->is_live )
1212             {
1213                 g_debug("Seeking live source in PLAYING state!");
1214                 _do_seek(worker,
1215                          GST_SEEK_TYPE_SET,
1216                          worker->seek_position,
1217                          FALSE,
1218                          NULL);
1219                 /* this has to be set as do_seek sets statechanges to FALSE
1220                   but we still want to inform that we're in PLAYING state */
1221                 worker->report_statechanges = TRUE;
1222                 /* seek position needs to be reset here for a live stream */
1223                 worker->seek_position = -1;
1224             }
1225
1226             /* Because live streams are sought in PLAYING state, we reset
1227                seek_position after all state transitions are completed. Normal
1228                streams resetting seek_position here is OK.  */
1229             if(worker->report_statechanges == FALSE || !worker->is_live)
1230             {
1231                 /* if seek was called, at this point it is really ended */
1232                 worker->seek_position = mafw_gst_renderer_seeker_process(worker->seeker);
1233             }
1234
1235             /* Signal state change if needed */
1236             _report_playing_state(worker);
1237
1238             /* Prevent blanking if we are playing video */
1239             if (worker->media.has_visual_content &&
1240                 worker->blanking__control_handler)
1241             {
1242                 worker->blanking__control_handler(worker, worker->owner, TRUE);
1243             }
1244
1245             /* Back to playing no longer in_ready (if ever was) */
1246             worker->in_ready = FALSE;
1247
1248             /* context framework adaptation starts */
1249             worker->context_nowplaying =
1250                 _set_context_map_value(worker->context_nowplaying,
1251                                        GST_TAG_LOCATION,
1252                                        worker->media.location);
1253             context_provider_set_map(CONTEXT_PROVIDER_KEY_NOWPLAYING,
1254                                      worker->context_nowplaying, FALSE);
1255             /* context framework adaptation ends */
1256
1257             /* Emit metadata. We wait until we reach the playing state because
1258              * this speeds up playback start time */
1259             _emit_metadatas(worker);
1260
1261             /* in any case the duration is verified, it may change with VBR media */
1262             _add_duration_seek_query_timeout(worker);
1263
1264             /* We've reached playing state, state changes are not reported
1265              * unless explicitly requested (e.g. by PAUSE request) seeking
1266              * in PLAYING does not cause state change reports
1267              */
1268             worker->report_statechanges = FALSE;
1269
1270             /* Delayed pause e.g. because of seek */
1271             if (worker->stay_paused) {
1272                 mafw_gst_renderer_worker_pause(worker);
1273             }
1274
1275             break;
1276         case GST_STATE_CHANGE_PAUSED_TO_READY:
1277             /* If we went to READY, we free the taglist and * deassign the
1278              * timout it */
1279             if (worker->in_ready) {
1280                 g_debug("changed to GST_STATE_READY");
1281                 worker->ready_timeout = 0;
1282                 _free_taglist(worker);
1283
1284                 if( worker->notify_ready_state_handler )
1285                 {
1286                     worker->notify_ready_state_handler(worker, worker->owner);
1287                 }
1288             }
1289             break;
1290         case GST_STATE_CHANGE_NULL_TO_READY:
1291             if(g_str_has_prefix(worker->media.location, STREAM_TYPE_MMSH) ||
1292                g_str_has_prefix(worker->media.location, STREAM_TYPE_MMS))
1293             {
1294                 GstElement *source = NULL;
1295                 g_object_get(worker->pipeline, "source", &source, NULL);
1296                 if(source)
1297                 {
1298                     gst_util_set_object_arg(G_OBJECT(source), "tcp-timeout", MAFW_GST_MMSH_TCP_TIMEOUT);
1299                     gst_object_unref(source);
1300                 }
1301                 else
1302                     g_warning("Failed to get source element from pipeline");
1303             }
1304             break;
1305         default:
1306             break;
1307     }
1308 }
1309
1310 static void _handle_duration(MafwGstRendererWorker *worker)
1311 {
1312     /* if we've got ongoing query timeout ignore this and in any case do it only in PAUSE/PLAYING */
1313     if( worker->duration_seek_timeout == 0
1314         && ( worker->state == GST_STATE_PAUSED || worker->state == GST_STATE_PLAYING) )
1315     {
1316         /* We want to check this quickly but not immediately */
1317         g_timeout_add_full(G_PRIORITY_DEFAULT,
1318                            MAFW_GST_RENDERER_WORKER_DURATION_AND_SEEKABILITY_FAST_TIMEOUT,
1319                            _query_duration_and_seekability_timeout,
1320                            worker,
1321                            NULL);
1322     }
1323 }
1324
1325 static void _emit_renderer_art(MafwGstRendererWorker *worker,
1326                                const GstTagList *list)
1327 {
1328     GstBuffer *buffer = NULL;
1329     const GValue *value = NULL;
1330
1331     g_return_if_fail(gst_tag_list_get_tag_size(list, GST_TAG_IMAGE) > 0);
1332
1333     value = gst_tag_list_get_value_index(list, GST_TAG_IMAGE, 0);
1334
1335     g_return_if_fail((value != NULL) && G_VALUE_HOLDS(value, GST_TYPE_BUFFER));
1336
1337     buffer = g_value_peek_pointer(value);
1338
1339     g_return_if_fail((buffer != NULL) && GST_IS_BUFFER(buffer));
1340
1341     _emit_gst_buffer_as_graphic_file(worker,
1342                                      buffer,
1343                                      WORKER_METADATA_KEY_RENDERER_ART_URI);
1344 }
1345
1346 static void value_dtor(gpointer value)
1347 {
1348
1349     if (G_IS_VALUE(value)) {
1350         g_value_unset(value);
1351         g_free(value);
1352     } else {
1353         g_value_array_free(value);
1354     }
1355
1356 }
1357
1358 static gboolean _current_metadata_add(MafwGstRendererWorker *worker,
1359                                       const gint key,
1360                                       GType type,
1361                                       const gpointer value)
1362 {
1363     GValue *new_gval;
1364     gboolean was_updated = FALSE;
1365
1366     if( value == NULL )
1367     {
1368         g_warning("Null value for metadata was tried to be set!");
1369         return was_updated;
1370     }
1371
1372     if (!worker->current_metadata) {
1373         worker->current_metadata = g_hash_table_new_full(g_direct_hash,
1374                                                          g_direct_equal,
1375                                                          NULL,
1376                                                          value_dtor);
1377     }
1378
1379     if (type == G_TYPE_VALUE_ARRAY) {
1380         GValueArray *values = (GValueArray *) value;
1381
1382         if (values->n_values == 1) {
1383             GValue *gval = g_value_array_get_nth(values, 0);
1384             new_gval = g_new0(GValue, 1);
1385             g_value_init(new_gval, G_VALUE_TYPE(gval));
1386             g_value_copy(gval, new_gval);
1387
1388             GValue *existing = (GValue*)g_hash_table_lookup(worker->current_metadata, (gpointer)key);
1389             if(!existing || (GST_VALUE_EQUAL != gst_value_compare(existing, new_gval)) )
1390             {
1391                 was_updated = TRUE;
1392             }
1393             g_hash_table_insert(worker->current_metadata,
1394                                 (gpointer)key,
1395                                 new_gval);
1396         } else {
1397             GValueArray *new_gvalues = g_value_array_copy(values);
1398
1399             GValueArray *existing = (GValueArray*)g_hash_table_lookup(worker->current_metadata, (gpointer)key);
1400
1401             if( existing
1402                 && new_gvalues->n_values == existing->n_values )
1403             {
1404                 guint size = new_gvalues->n_values;
1405
1406                 guint i = 0;
1407                 for( ; i < size; ++i )
1408                 {
1409                     GValue *newVal = g_value_array_get_nth(new_gvalues, i);
1410                     GValue *existingVal = g_value_array_get_nth(existing, i);
1411                     if( GST_VALUE_EQUAL != gst_value_compare(newVal, existingVal) )
1412                     {
1413                         was_updated = TRUE;
1414                         break;
1415                     }
1416                 }
1417             }
1418             else
1419             {
1420                 was_updated = TRUE;
1421             }
1422
1423             g_hash_table_insert(worker->current_metadata,
1424                                 (gpointer)key,
1425                                 new_gvalues);
1426         }
1427
1428         return was_updated;
1429     }
1430
1431     new_gval = g_new0(GValue, 1);
1432
1433     if (_set_value(new_gval, type, value) == FALSE)
1434     {
1435         g_warning("Metadata type: %i is not being handled", type);
1436         return was_updated;
1437     }
1438
1439     GValue *existing = (GValue*)g_hash_table_lookup(worker->current_metadata, (gpointer)key);
1440     if(!existing || (GST_VALUE_EQUAL != gst_value_compare(existing, new_gval)) )
1441     {
1442         was_updated = TRUE;
1443     }
1444     g_hash_table_insert(worker->current_metadata, (gpointer)key, new_gval);
1445
1446     return was_updated;
1447
1448 }
1449
1450 static GHashTable* _build_tagmap(void)
1451 {
1452     GHashTable *hash_table = NULL;
1453
1454     hash_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
1455
1456     g_hash_table_insert(hash_table, g_strdup(GST_TAG_TITLE),
1457                         (gpointer)WORKER_METADATA_KEY_TITLE);
1458     g_hash_table_insert(hash_table, g_strdup(GST_TAG_ARTIST),
1459                         (gpointer)WORKER_METADATA_KEY_ARTIST);
1460     g_hash_table_insert(hash_table, g_strdup(GST_TAG_AUDIO_CODEC),
1461                         (gpointer)WORKER_METADATA_KEY_AUDIO_CODEC);
1462     g_hash_table_insert(hash_table, g_strdup(GST_TAG_VIDEO_CODEC),
1463                         (gpointer)WORKER_METADATA_KEY_VIDEO_CODEC);
1464     g_hash_table_insert(hash_table, g_strdup(GST_TAG_BITRATE),
1465                         (gpointer)WORKER_METADATA_KEY_BITRATE);
1466     g_hash_table_insert(hash_table, g_strdup(GST_TAG_LANGUAGE_CODE),
1467                         (gpointer)WORKER_METADATA_KEY_ENCODING);
1468     g_hash_table_insert(hash_table, g_strdup(GST_TAG_ALBUM),
1469                         (gpointer)WORKER_METADATA_KEY_ALBUM);
1470     g_hash_table_insert(hash_table, g_strdup(GST_TAG_GENRE),
1471                         (gpointer)WORKER_METADATA_KEY_GENRE);
1472     g_hash_table_insert(hash_table, g_strdup(GST_TAG_TRACK_NUMBER),
1473                         (gpointer)WORKER_METADATA_KEY_TRACK);
1474     g_hash_table_insert(hash_table, g_strdup(GST_TAG_ORGANIZATION),
1475                         (gpointer)WORKER_METADATA_KEY_ORGANIZATION);
1476     g_hash_table_insert(hash_table, g_strdup(GST_TAG_IMAGE),
1477                         (gpointer)WORKER_METADATA_KEY_RENDERER_ART_URI);
1478
1479     return hash_table;
1480 }
1481
1482 /*
1483  * Sets values to given context framework map, allocates it when map is NULL.
1484  */
1485 gpointer _set_context_map_value(gpointer map,
1486                                 const gchar *tag,
1487                                 const gchar *value)
1488 {
1489
1490     if (map == NULL)
1491     {
1492         map = context_provider_map_new();
1493     }
1494
1495     if (g_str_equal(tag, GST_TAG_LOCATION))
1496     {
1497         context_provider_map_set_string(map,
1498                                         CONTEXT_PROVIDER_KEY_NOWPLAYING_RESOURCE,
1499                                         value);
1500     }
1501     else if (g_str_equal(tag, GST_TAG_TITLE))
1502     {
1503         context_provider_map_set_string(map,
1504                                         CONTEXT_PROVIDER_KEY_NOWPLAYING_TITLE,
1505                                         value);
1506     }
1507     else if (g_str_equal(tag, GST_TAG_ARTIST))
1508     {
1509         context_provider_map_set_string(map,
1510                                         CONTEXT_PROVIDER_KEY_NOWPLAYING_ARTIST,
1511                                         value);
1512     }
1513     else if (g_str_equal(tag, GST_TAG_ALBUM))
1514     {
1515         context_provider_map_set_string(map,
1516                                         CONTEXT_PROVIDER_KEY_NOWPLAYING_ALBUM,
1517                                         value);
1518     }
1519     else if (g_str_equal(tag, GST_TAG_GENRE))
1520     {
1521         context_provider_map_set_string(map,
1522                                         CONTEXT_PROVIDER_KEY_NOWPLAYING_GENRE,
1523                                         value);
1524     }
1525
1526     return map;
1527
1528 }
1529
1530 /*
1531  * Emits metadata-changed signals for gst tags.
1532  */
1533 static void _emit_tag(const GstTagList *list,
1534                       const gchar *tag,
1535                       MafwGstRendererWorker *worker)
1536 {
1537     /* Mapping between Gst <-> MAFW metadata tags
1538      * NOTE: This assumes that GTypes matches between GST and MAFW. */
1539     static GHashTable *tagmap = NULL;
1540     gint i, count;
1541     gint mafwtag;
1542     GType type;
1543     GValueArray *values;
1544
1545     if (tagmap == NULL) {
1546         tagmap = _build_tagmap();
1547     }
1548
1549     g_debug("tag: '%s' (type: %s)", tag, g_type_name(gst_tag_get_type(tag)));
1550     /* Is there a mapping for this tag? */
1551     mafwtag = (gint)g_hash_table_lookup(tagmap, tag);
1552     if (!mafwtag)
1553         return;
1554
1555     if (mafwtag == WORKER_METADATA_KEY_RENDERER_ART_URI) {
1556         _emit_renderer_art(worker, list);
1557         return;
1558     }
1559
1560     /* Build a value array of this tag.  We need to make sure that strings
1561      * are UTF-8.  GstTagList API says that the value is always UTF8, but it
1562      * looks like the ID3 demuxer still might sometimes produce non-UTF-8
1563      * strings. */
1564     count = gst_tag_list_get_tag_size(list, tag);
1565
1566     type = gst_tag_get_type(tag);
1567     values = g_value_array_new(count);
1568     for (i = 0; i < count; ++i) {
1569         GValue *v = (GValue *)
1570             gst_tag_list_get_value_index(list, tag, i);
1571         if (type == G_TYPE_STRING) {
1572             gchar *orig, *utf8;
1573
1574             gboolean tagExists = gst_tag_list_get_string_index(list, tag, i, &orig);
1575             UNUSED(tagExists); //TODO what if tag does not exists?
1576             if (convert_utf8(orig, &utf8)) {
1577                 GValue utf8gval;
1578                 memset(&utf8gval, 0, sizeof(utf8gval));
1579
1580                 g_value_init(&utf8gval, G_TYPE_STRING);
1581                 g_value_take_string(&utf8gval, utf8);
1582                 g_value_array_append(values, &utf8gval);
1583                 g_value_unset(&utf8gval);
1584             }
1585             /* context framework adaptation starts */
1586             worker->context_nowplaying =
1587                 _set_context_map_value(worker->context_nowplaying,
1588                                        tag,
1589                                        orig);
1590             /* context framework adaptation ends */
1591             g_free(orig);
1592         } else if (type == G_TYPE_UINT) {
1593             GValue intgval;
1594             memset(&intgval, 0, sizeof(intgval));
1595
1596             g_value_init(&intgval, G_TYPE_INT);
1597             g_value_transform(v, &intgval);
1598             g_value_array_append(values, &intgval);
1599             g_value_unset(&intgval);
1600         } else {
1601             g_value_array_append(values, v);
1602         }
1603     }
1604
1605     /* context framework adaptation starts */
1606     context_provider_set_map(CONTEXT_PROVIDER_KEY_NOWPLAYING,
1607                              worker->context_nowplaying, FALSE);
1608     /* context framework adaptation ends */
1609
1610     /* Add the info to the current metadata. */
1611     gboolean changed = _current_metadata_add(worker,
1612                                              mafwtag,
1613                                              G_TYPE_VALUE_ARRAY,
1614                                              (const gpointer) values);
1615
1616     /* Emit the metadata. */
1617     if (changed && worker->notify_metadata_handler)
1618     {
1619         worker->notify_metadata_handler(worker,
1620                                         worker->owner,
1621                                         mafwtag,
1622                                         G_TYPE_VALUE_ARRAY,
1623                                         values);
1624     }
1625
1626     g_value_array_free(values);
1627 }
1628
1629 /**
1630  * Collect tag-messages, parse it later, when playing is ongoing
1631  */
1632 static void _handle_tag(MafwGstRendererWorker *worker, GstMessage *msg)
1633 {
1634     /* Do not emit metadata until we get to PLAYING state to speed up playback
1635      * start */
1636     if (worker->tag_list == NULL)
1637         worker->tag_list = g_ptr_array_new();
1638     g_ptr_array_add(worker->tag_list, gst_message_ref(msg));
1639
1640     /* Some tags come in playing state, so in this case we have to emit them
1641      * right away (example: radio stations) */
1642     if (worker->state == GST_STATE_PLAYING) {
1643         _emit_metadatas(worker);
1644     }
1645 }
1646
1647 /**
1648  * Parses the list of tag-messages
1649  */
1650 static void _parse_tagmsg(GstMessage *msg, MafwGstRendererWorker *worker)
1651 {
1652     GstTagList *new_tags;
1653
1654     gst_message_parse_tag(msg, &new_tags);
1655     gst_tag_list_foreach(new_tags, (gpointer)_emit_tag, worker);
1656     gst_tag_list_free(new_tags);
1657     gst_message_unref(msg);
1658 }
1659
1660 /**
1661  * Parses the collected tag messages, and emits the metadatas
1662  */
1663 static void _emit_metadatas(MafwGstRendererWorker *worker)
1664 {
1665     if (worker->tag_list != NULL)
1666     {
1667         g_ptr_array_foreach(worker->tag_list, (GFunc)_parse_tagmsg, worker);
1668         g_ptr_array_free(worker->tag_list, TRUE);
1669         worker->tag_list = NULL;
1670     }
1671 }
1672
1673 static void _handle_buffering(MafwGstRendererWorker *worker, GstMessage *msg)
1674 {
1675     /* We set smaller buffer for mms streams */
1676     if((g_str_has_prefix(worker->media.location, STREAM_TYPE_MMSH) || g_str_has_prefix(worker->media.location, STREAM_TYPE_MMS))
1677         && worker->state != GST_STATE_PLAYING && !worker->buffering)
1678     {
1679         if(worker->queue)
1680         {
1681             g_object_set(worker->queue, "high-percent", 30, NULL);
1682         }
1683         else
1684         {
1685             g_warning("Queue2 element doesn't exist!");
1686         }
1687     }
1688
1689     /* We can ignore buffering messages when we are in READY state or when going to it */
1690     if(worker->state == GST_STATE_READY || worker->ready_timeout != 0 )
1691     {
1692         worker->buffering = TRUE;
1693         return;
1694     }
1695
1696     gint percent;
1697     gst_message_parse_buffering(msg, &percent);
1698     g_debug("buffering: %d", percent);
1699
1700     /* No state management needed for live pipelines */
1701     if (!worker->is_live) {
1702         worker->buffering = TRUE;
1703         if (percent < 100 && worker->state == GST_STATE_PLAYING) {
1704             /* If we need to buffer more, we set larger buffer */
1705             if(g_str_has_prefix(worker->media.location, STREAM_TYPE_MMSH) || g_str_has_prefix(worker->media.location, STREAM_TYPE_MMS))
1706             {
1707                 if(worker->queue)
1708                 {
1709                     g_object_set(worker->queue, "high-percent", 100, NULL);
1710                 }
1711                 else
1712                 {
1713                     g_warning("Queue2 element doesn't exist!");
1714                 }
1715             }
1716             g_debug("setting pipeline to PAUSED not to wolf the buffer down");
1717
1718             //if there's no requested state transitions i.e. resume/pause let's keep this quiet
1719             if( gst_element_get_state(worker->pipeline, NULL, NULL, 0) == GST_STATE_CHANGE_SUCCESS )
1720             {
1721                 worker->report_statechanges = FALSE;
1722             }
1723
1724             /* We can't call _pause() here, since it sets the
1725              * "report_statechanges" to TRUE.  We don't want that, application
1726              * doesn't need to know that internally the state changed to PAUSED.
1727              */
1728             gst_element_set_state(worker->pipeline, GST_STATE_PAUSED);
1729         }
1730
1731         if (percent >= 100) {
1732             /* On buffering we go to PAUSED, so here we move back to PLAYING */
1733             worker->buffering = FALSE;
1734             if (worker->state == GST_STATE_PAUSED)
1735             {
1736                 /* If buffering more than once, do this only the first time we
1737                  * are done with buffering */
1738                 if (worker->prerolling)
1739                 {
1740                     g_debug("buffering concluded during prerolling");
1741                     _finalize_startup(worker);
1742                     _do_play(worker);
1743                     /* Send the paused notification */
1744                     if (worker->stay_paused &&
1745                         worker->notify_pause_handler)
1746                     {
1747                         worker->notify_pause_handler(worker, worker->owner);
1748                     }
1749                     worker->prerolling = FALSE;
1750                 }
1751                 /* if eos has been reached no automatic playing should be done
1752                    only on resume request e.g. eos reached -> seek requested => stays paused after seek&buffering completed */
1753                 else if (!worker->stay_paused && !worker->eos)
1754                 {
1755                     g_debug("buffering concluded, setting "
1756                             "pipeline to PLAYING again");
1757                     worker->report_statechanges = TRUE;
1758                     gst_element_set_state(worker->pipeline, GST_STATE_PLAYING);
1759                 }
1760             }
1761             /* if there's no pending state changes and we're really in PLAYING state... */
1762             else if (gst_element_get_state(worker->pipeline, NULL, NULL, 0) == GST_STATE_CHANGE_SUCCESS
1763                        && worker->state == GST_STATE_PLAYING)
1764             {
1765                 g_debug("buffering concluded, signalling state change");
1766                 /* In this case we got a PLAY command while buffering, likely
1767                  * because it was issued before we got the first buffering
1768                  * signal.  The UI should not do this, but if it does, we have
1769                  * to signal that we have executed the state change, since in
1770                  * _handle_state_changed we do not do anything if we are
1771                  * buffering */
1772                 if (worker->report_statechanges &&
1773                     worker->notify_play_handler) {
1774                     worker->notify_play_handler(worker, worker->owner);
1775                 }
1776                 _add_duration_seek_query_timeout(worker);
1777
1778             }
1779             /* has somebody requested pause transition? */
1780             else if( !worker->stay_paused )
1781             {
1782                 /* we're in PLAYING but pending for paused state change.
1783                    Let's request new state change to override the pause */
1784                 gst_element_set_state(worker->pipeline, GST_STATE_PLAYING);
1785             }
1786         }
1787     }
1788
1789     /* Send buffer percentage */
1790     if (worker->notify_buffer_status_handler)
1791         worker->notify_buffer_status_handler(worker, worker->owner, percent);
1792 }
1793
1794 static void _handle_element_msg(MafwGstRendererWorker *worker, GstMessage *msg)
1795 {
1796     /* Only HelixBin sends "resolution" messages. */
1797     if (gst_structure_has_name(msg->structure, "resolution") &&
1798         _handle_video_info(worker, msg->structure))
1799     {
1800         worker->media.has_visual_content = TRUE;
1801         set_dolby_video_property(worker, worker->config->mobile_surround_video.state);
1802         set_dolby_video_sound_property(worker, worker->config->mobile_surround_video.room, TRUE);
1803         set_dolby_video_sound_property(worker, worker->config->mobile_surround_video.color, FALSE);
1804     }
1805     /* We do RTSP redirect when we try to play .sdp streams */
1806     else if(gst_structure_has_name(msg->structure, "redirect"))
1807     {
1808         /* "new-location" contains the rtsp uri what we are going to play */
1809         mafw_gst_renderer_worker_play(worker, gst_structure_get_string(msg->structure, "new-location"));
1810     }
1811
1812 }
1813
1814 static GError * _get_specific_missing_plugin_error(GstMessage *msg)
1815 {
1816     const GstStructure *gst_struct;
1817     const gchar *type;
1818
1819     GError *error;
1820     gchar *desc;
1821
1822     desc = gst_missing_plugin_message_get_description(msg);
1823
1824     gst_struct = gst_message_get_structure(msg);
1825     type = gst_structure_get_string(gst_struct, "type");
1826
1827     if ((type) && ((strcmp(type, MAFW_GST_MISSING_TYPE_DECODER) == 0) ||
1828                    (strcmp(type, MAFW_GST_MISSING_TYPE_ENCODER) == 0))) {
1829
1830         /* Missing codec error. */
1831         const GValue *val;
1832         const GstCaps *caps;
1833         GstStructure *caps_struct;
1834         const gchar *mime;
1835
1836         val = gst_structure_get_value(gst_struct, "detail");
1837         caps = gst_value_get_caps(val);
1838         caps_struct = gst_caps_get_structure(caps, 0);
1839         mime = gst_structure_get_name(caps_struct);
1840
1841         if (g_strrstr(mime, "video")) {
1842             error = g_error_new_literal(WORKER_ERROR,
1843                                         WORKER_ERROR_VIDEO_CODEC_NOT_FOUND,
1844                                         desc);
1845         } else if (g_strrstr(mime, "audio")) {
1846             error = g_error_new_literal(WORKER_ERROR,
1847                                         WORKER_ERROR_AUDIO_CODEC_NOT_FOUND,
1848                                         desc);
1849         } else {
1850             error = g_error_new_literal(WORKER_ERROR,
1851                                         WORKER_ERROR_CODEC_NOT_FOUND,
1852                                         desc);
1853         }
1854     } else {
1855         /* Unsupported type error. */
1856         error = g_error_new(WORKER_ERROR,
1857                             WORKER_ERROR_UNSUPPORTED_TYPE,
1858                             "missing plugin: %s", desc);
1859     }
1860
1861     g_free(desc);
1862
1863     return error;
1864 }
1865
1866 /*
1867  * Asynchronous message handler.  It gets removed from if it returns FALSE.
1868  */
1869 static gboolean _async_bus_handler(GstBus *bus,
1870                                    GstMessage *msg,
1871                                    MafwGstRendererWorker *worker)
1872 {
1873
1874     UNUSED(bus);
1875
1876     /* No need to handle message if error has already occured. */
1877     if (worker->is_error)
1878         return TRUE;
1879
1880     /* Handle missing-plugin (element) messages separately, relaying more
1881      * details. */
1882     if (gst_is_missing_plugin_message(msg)) {
1883         GError *err = _get_specific_missing_plugin_error(msg);
1884         /* FIXME?: for some reason, calling the error handler directly
1885          * (_send_error) causes problems.  On the other hand, turning
1886          * the error into a new GstMessage and letting the next
1887          * iteration handle it seems to work. */
1888         _post_error(worker, err);
1889         return TRUE;
1890     }
1891     switch (GST_MESSAGE_TYPE(msg)) {
1892         case GST_MESSAGE_ERROR:
1893             if (!worker->is_error) {
1894                 gchar *debug;
1895                 GError *err;
1896                 debug = NULL;
1897                 gst_message_parse_error(msg, &err, &debug);
1898                 g_debug("gst error: domain = %s, code = %d, "
1899                         "message = '%s', debug = '%s'",
1900                         g_quark_to_string(err->domain), err->code, err->message, debug);
1901                 if (debug)
1902                 {
1903                     g_free(debug);
1904                 }
1905                                 //decodebin2 uses the error only to report text files
1906                 if (err->code == GST_STREAM_ERROR_WRONG_TYPE && g_str_has_prefix(GST_MESSAGE_SRC_NAME(msg), "decodebin2"))
1907                 {
1908                     err->code = WORKER_ERROR_POSSIBLY_PLAYLIST_TYPE;
1909                 }
1910
1911                 _send_error(worker, err);
1912             }
1913             break;
1914         case GST_MESSAGE_EOS:
1915             if (!worker->is_error) {
1916                 worker->eos = TRUE;
1917                 worker->seek_position = -1;
1918                 if (worker->notify_eos_handler)
1919                 {
1920                     worker->notify_eos_handler(worker, worker->owner);
1921                 }
1922             }
1923             break;
1924         case GST_MESSAGE_TAG:
1925             _handle_tag(worker, msg);
1926             break;
1927         case GST_MESSAGE_BUFFERING:
1928             _handle_buffering(worker, msg);
1929             break;
1930         case GST_MESSAGE_DURATION:
1931             /* in ready state we might not get correct seekability info */
1932             if (!worker->in_ready)
1933             {
1934                 _handle_duration(worker);
1935             }
1936             break;
1937         case GST_MESSAGE_ELEMENT:
1938             _handle_element_msg(worker, msg);
1939             break;
1940         case GST_MESSAGE_STATE_CHANGED:
1941             if ((GstElement *)GST_MESSAGE_SRC(msg) == worker->pipeline)
1942                 _handle_state_changed(msg, worker);
1943             break;
1944         case GST_MESSAGE_APPLICATION:
1945             if (gst_structure_has_name(gst_message_get_structure(msg), "ckey"))
1946             {
1947                 _emit_property(worker,
1948                                WORKER_PROPERTY_COLORKEY,
1949                                G_TYPE_INT,
1950                                &worker->colorkey);
1951             }
1952         default:
1953             break;
1954     }
1955     return TRUE;
1956 }
1957
1958 /* NOTE this function will possibly be called from a different thread than the
1959  * glib main thread. */
1960 static void _stream_info_cb(GstObject *pipeline, GParamSpec *unused,
1961                             MafwGstRendererWorker *worker)
1962 {
1963     UNUSED(pipeline);
1964     UNUSED(unused);
1965
1966     g_debug("stream-info changed");
1967     _parse_stream_info(worker);
1968 }
1969
1970 static void _element_added_cb(GstBin *bin, GstElement *element,
1971                               MafwGstRendererWorker *worker)
1972 {
1973     UNUSED(bin);
1974     gchar *element_name;
1975
1976     element_name = gst_element_get_name(element);
1977     if(g_str_has_prefix(element_name, "uridecodebin") ||
1978        g_str_has_prefix(element_name, "decodebin2"))
1979     {
1980         g_signal_connect(element, "element-added",
1981                          G_CALLBACK(_element_added_cb), worker);
1982     }
1983     else if(g_str_has_prefix(element_name, "sdpdemux"))
1984     {
1985         g_object_set(element, "redirect", FALSE, NULL);
1986     }
1987     else if(g_str_has_prefix(element_name, "queue2"))
1988     {
1989         worker->queue = element;
1990     }
1991     g_free(element_name);
1992 }
1993
1994 /*
1995  * Start to play the media
1996  */
1997 static void _start_play(MafwGstRendererWorker *worker)
1998 {
1999     GstStateChangeReturn state_change_info;
2000     worker->stay_paused = FALSE;
2001     char *autoload_sub = NULL;
2002
2003     g_assert(worker->pipeline);
2004     g_object_set(G_OBJECT(worker->pipeline),
2005                  "uri", worker->media.location, NULL);
2006
2007     if (worker->config->autoload_subtitles) {
2008         autoload_sub = uri_get_subtitle_uri(worker->media.location);
2009         if (autoload_sub) {
2010             g_debug("SUBURI: %s", autoload_sub);
2011             g_object_set(G_OBJECT(worker->pipeline),
2012                          "suburi", autoload_sub,
2013                          "subtitle-font-desc", worker->config->subtitle_font,
2014                          "subtitle-encoding", worker->config->subtitle_encoding,
2015                          NULL);
2016
2017             gst_element_set_state(worker->pipeline, GST_STATE_READY);
2018             g_free(autoload_sub);
2019         }
2020     } else {
2021         g_object_set(G_OBJECT(worker->pipeline), "suburi", NULL, NULL);
2022     }
2023
2024     g_debug("URI: %s", worker->media.location);
2025     g_debug("setting pipeline to PAUSED");
2026
2027     worker->report_statechanges = TRUE;
2028     state_change_info = gst_element_set_state(worker->pipeline,
2029                                               GST_STATE_PAUSED);
2030     if (state_change_info == GST_STATE_CHANGE_NO_PREROLL) {
2031         /* FIXME:  for live sources we may have to handle buffering and
2032          * prerolling differently */
2033         g_debug ("Source is live!");
2034         worker->is_live = TRUE;
2035     }
2036     worker->prerolling = TRUE;
2037
2038     worker->is_stream = uri_is_stream(worker->media.location);
2039
2040 }
2041
2042 /*
2043  * Constructs gst pipeline
2044  *
2045  * FIXME: Could the same pipeline be used for playing all media instead of
2046  *  constantly deleting and reconstructing it again?
2047  */
2048 static void _construct_pipeline(MafwGstRendererWorker *worker, configuration *config)
2049 {
2050     g_debug("constructing pipeline");
2051     g_assert(worker != NULL);
2052
2053     /* Return if we have already one */
2054     if (worker->pipeline)
2055         return;
2056
2057     _free_taglist(worker);
2058
2059     g_debug("Creating a new instance of playbin2");
2060     worker->pipeline = gst_element_factory_make("playbin2", "playbin");
2061     if (worker->pipeline == NULL)
2062     {
2063         /* Let's try with playbin */
2064         g_warning ("playbin2 failed, falling back to playbin");
2065         worker->pipeline = gst_element_factory_make("playbin", "playbin");
2066
2067         if (worker->pipeline) {
2068             /* Use nwqueue only for non-rtsp and non-mms(h) streams. */
2069             gboolean use_nw;
2070             use_nw = worker->media.location &&
2071                 !g_str_has_prefix(worker->media.location, "rtsp://") &&
2072                 !g_str_has_prefix(worker->media.location, "mms://") &&
2073                 !g_str_has_prefix(worker->media.location, "mmsh://");
2074
2075             g_debug("playbin using network queue: %d", use_nw);
2076
2077             gst_object_ref_sink(worker->pipeline);
2078             /* These need a modified version of playbin. */
2079             g_object_set(G_OBJECT(worker->pipeline),
2080                          "nw-queue", use_nw,
2081                          "no-video-transform", TRUE,
2082                          NULL);
2083
2084         }
2085     }
2086
2087     if (!worker->pipeline) {
2088         g_critical("failed to create playback pipeline");
2089         _send_error(worker,
2090                     g_error_new(WORKER_ERROR,
2091                                 WORKER_ERROR_UNABLE_TO_PERFORM,
2092                                 "Could not create pipeline"));
2093         g_assert_not_reached();
2094     }
2095
2096     worker->bus = gst_pipeline_get_bus(GST_PIPELINE(worker->pipeline));
2097     gst_bus_set_sync_handler(worker->bus,
2098                              (GstBusSyncHandler)_sync_bus_handler,
2099                              worker);
2100     worker->async_bus_id = gst_bus_add_watch_full(
2101         worker->bus,G_PRIORITY_HIGH,
2102         (GstBusFunc)_async_bus_handler,
2103         worker, NULL);
2104
2105     /* Listen for changes in stream-info object to find out whether the media
2106      * contains video and throw error if application has not provided video
2107      * window. */
2108     g_signal_connect(worker->pipeline, "notify::stream-info",
2109                      G_CALLBACK(_stream_info_cb), worker);
2110
2111     g_signal_connect(worker->pipeline, "element-added",
2112                      G_CALLBACK(_element_added_cb), worker);
2113
2114     /* Set audio and video sinks ourselves. We create and configure them only
2115      * once. */
2116     if (!worker->asink) {
2117         const gchar *sink = g_getenv("AUDIO_SINK");
2118         worker->asink = gst_element_factory_make(sink?sink: worker->config->asink, NULL);
2119         if (!worker->asink){
2120             worker->asink = gst_element_factory_make("alsasink", NULL);
2121         }
2122         if (!worker->asink) {
2123             g_critical("Failed to create pipeline audio sink");
2124             _send_error(worker,
2125                         g_error_new(WORKER_ERROR,
2126                                     WORKER_ERROR_UNABLE_TO_PERFORM,
2127                                     "Could not create audio sink"));
2128             g_assert_not_reached();
2129         }
2130         g_debug("MafwGstRendererWorker: Using following buffer-time: %lld and latency-time: %lld",
2131                 config->buffer_time,
2132                 config->latency_time);
2133         gst_object_ref_sink(worker->asink);
2134         g_object_set(worker->asink,
2135                      "buffer-time", config->buffer_time,
2136                      "latency-time", config->latency_time,
2137                      NULL);
2138     }
2139
2140     if (worker->config->use_dhmmixer && !worker->amixer)
2141     {
2142         worker->amixer = gst_element_factory_make("nokiadhmmix", NULL);
2143         if( !worker->amixer )
2144         {
2145             g_warning("Could not create dhmmixer, falling back to basic audiosink!");
2146         }
2147     }
2148
2149     if( worker->config->use_dhmmixer && worker->amixer && !worker->audiobin )
2150     {
2151         worker->audiobin = gst_bin_new("audiobin");
2152         if( worker->audiobin )
2153         {
2154             gst_bin_add(GST_BIN (worker->audiobin), worker->amixer);
2155             gst_bin_add(GST_BIN (worker->audiobin), worker->asink);
2156             gst_element_link(worker->amixer, worker->asink);
2157             GstPad *pad;
2158             pad = gst_element_get_static_pad (worker->amixer, "sink");
2159             gst_element_add_pad (worker->audiobin, gst_ghost_pad_new ("sink", pad));
2160             gst_object_unref (GST_OBJECT (pad));
2161
2162             gst_object_ref(worker->audiobin);
2163
2164             /* Use Dolby music settings by default */
2165             set_dolby_music_property(worker, worker->config->mobile_surround_music.state);
2166             set_dolby_music_sound_property(worker, worker->config->mobile_surround_music.room, TRUE);
2167             set_dolby_music_sound_property(worker, worker->config->mobile_surround_music.color, FALSE);
2168         }
2169         else
2170         {
2171             gst_object_ref_sink(worker->asink);
2172             gst_object_sink(worker->amixer);
2173             g_warning("Could not create audiobin! Falling back to basic audio-sink!");
2174         }
2175     }
2176
2177
2178     if( worker->config->use_dhmmixer && worker->amixer && worker->audiobin )
2179     {
2180         g_object_set(worker->pipeline,
2181                  "audio-sink", worker->audiobin,
2182                  "flags", worker->config->flags,
2183                   NULL);
2184     }
2185     else
2186     {
2187         g_object_set(worker->pipeline,
2188                  "audio-sink", worker->asink,
2189                  "flags", worker->config->flags,
2190                   NULL);
2191     }
2192
2193     if( worker->pipeline )
2194     {
2195         mafw_gst_renderer_seeker_set_pipeline(worker->seeker, worker->pipeline);
2196
2197         if( worker->vsink && worker->xid != 0 )
2198         {
2199             g_object_set(worker->pipeline,
2200                          "video-sink", worker->vsink,
2201                          NULL);
2202         }
2203     }
2204
2205     if (!worker->tsink) {
2206         worker->tsink = gst_element_factory_make("textoverlay", NULL);
2207         if (!worker->tsink) {
2208             g_critical("Failed to create pipeline text sink");
2209             _send_error(worker,
2210                         g_error_new(WORKER_ERROR,
2211                                     WORKER_ERROR_UNABLE_TO_PERFORM,
2212                                     "Could not create text sink"));
2213             g_assert_not_reached();
2214         }
2215         gst_object_ref(worker->tsink);
2216     }
2217     g_object_set(worker->pipeline, "text-sink", worker->tsink, NULL);
2218 }
2219
2220 guint check_dolby_audioroute(MafwGstRendererWorker *worker, guint prop) {
2221     if (g_slist_find(worker->destinations,
2222                      GINT_TO_POINTER(WORKER_OUTPUT_BLUETOOTH_AUDIO)) ||
2223         g_slist_find(worker->destinations,
2224                      GINT_TO_POINTER(WORKER_OUTPUT_HEADPHONE_JACK)))
2225     {
2226         return prop;
2227     }
2228     else
2229     {
2230         return 0;
2231     }
2232 }
2233
2234 void set_dolby_music_property(MafwGstRendererWorker *worker, guint prop) {
2235     worker->config->mobile_surround_music.state = prop;
2236     if (worker->amixer && !worker->media.has_visual_content) {
2237         GValue a;
2238         guint tempprop = check_dolby_audioroute(worker, prop);
2239         if (_set_value(&a, G_TYPE_UINT, &tempprop))
2240         {
2241             g_object_set_property(G_OBJECT(worker->amixer), "mobile-surround", &a);
2242             g_value_unset (&a);
2243         }
2244     }
2245 }
2246
2247 void set_dolby_music_sound_property(MafwGstRendererWorker *worker, gint prop, gboolean isRoomProperty) {
2248     if (isRoomProperty) {
2249         worker->config->mobile_surround_music.room = prop;
2250     } else {
2251         worker->config->mobile_surround_music.color = prop;
2252     }
2253     if (worker->amixer && !worker->media.has_visual_content) {
2254         GValue a;
2255
2256         if (_set_value(&a, G_TYPE_UINT, &prop))
2257         {
2258             if (isRoomProperty) {
2259                 g_object_set_property(G_OBJECT(worker->amixer), "room-size", &a);
2260             } else {
2261                 g_object_set_property(G_OBJECT(worker->amixer), "brightness", &a);
2262             }
2263             g_value_unset (&a);
2264         }
2265     }
2266 }
2267
2268 void set_dolby_video_property(MafwGstRendererWorker *worker, guint prop) {
2269     worker->config->mobile_surround_video.state = prop;
2270     if (worker->amixer && worker->media.has_visual_content) {
2271         GValue a;
2272         guint tempprop = check_dolby_audioroute(worker, prop);
2273         if (_set_value(&a, G_TYPE_UINT, &tempprop))
2274         {
2275             g_object_set_property(G_OBJECT(worker->amixer), "mobile-surround", &a);
2276             g_value_unset (&a);
2277         }
2278     }
2279 }
2280
2281 void set_dolby_video_sound_property(MafwGstRendererWorker *worker, gint prop, gboolean isRoomProperty) {
2282     if (isRoomProperty) {
2283         worker->config->mobile_surround_video.room = prop;
2284     } else {
2285         worker->config->mobile_surround_video.color = prop;
2286     }
2287     if (worker->amixer && worker->media.has_visual_content) {
2288         GValue a;
2289
2290         if (_set_value(&a, G_TYPE_UINT, &prop))
2291         {
2292             if (isRoomProperty) {
2293                 g_object_set_property(G_OBJECT(worker->amixer), "room-size", &a);
2294             } else {
2295                 g_object_set_property(G_OBJECT(worker->amixer), "brightness", &a);
2296             }
2297             g_value_unset (&a);
2298         }
2299     }
2300 }
2301
2302 /*
2303  * @seek_type: GstSeekType
2304  * @position: Time in seconds where to seek
2305  * @key_frame_seek: True if this is a key frame based seek
2306  * @error: Possible error that is set and returned
2307  */
2308 static void _do_seek(MafwGstRendererWorker *worker,
2309                      GstSeekType seek_type,
2310                      gint position,
2311                      gboolean key_frame_seek,
2312                      GError **error)
2313 {
2314     gboolean ret;
2315     gint64 spos;
2316     GstSeekFlags flags = GST_SEEK_FLAG_FLUSH;
2317
2318     g_assert(worker != NULL);
2319
2320     if (!worker->media.seekable == SEEKABILITY_SEEKABLE)
2321     {
2322         goto err;
2323     }
2324
2325     /* According to the docs, relative seeking is not so easy:
2326     GST_SEEK_TYPE_CUR - change relative to currently configured segment.
2327     This can't be used to seek relative to the current playback position -
2328     do a position query, calculate the desired position and then do an
2329     absolute position seek instead if that's what you want to do. */
2330     if (seek_type == GST_SEEK_TYPE_CUR)
2331     {
2332         gint curpos = mafw_gst_renderer_worker_get_position(worker);
2333         position = curpos + position;
2334         seek_type = GST_SEEK_TYPE_SET;
2335     }
2336
2337     if (position < 0) {
2338         position = 0;
2339     }
2340
2341     worker->seek_position = position;
2342
2343     if (worker->state != GST_STATE_PLAYING && worker->state != GST_STATE_PAUSED )
2344     {
2345         g_debug("_do_seek: Not in playing or paused state, seeking delayed.");
2346         return;
2347     }
2348     else if( worker->is_live && worker->state == GST_STATE_PAUSED )
2349     {
2350         g_debug("_do_seek: Live source can be seeked only in playing state, seeking delayed!");
2351         return;
2352     }
2353
2354     worker->report_statechanges = FALSE;
2355
2356     if (key_frame_seek == TRUE)
2357     {
2358         /* tries to do key frame seeks at least with some change */
2359         ret = mafw_gst_renderer_seeker_seek_to(worker->seeker, worker->seek_position);
2360     }
2361     else
2362     {
2363         spos = (gint64)position * GST_SECOND;
2364         g_debug("seek: type = %d, offset = %lld", seek_type, spos);
2365
2366         /* exact seek */
2367         ret = gst_element_seek(worker->pipeline,
2368                                1.0,
2369                                GST_FORMAT_TIME,
2370                                flags,
2371                                seek_type,
2372                                spos,
2373                                GST_SEEK_TYPE_NONE,
2374                                GST_CLOCK_TIME_NONE);
2375     }
2376
2377     if (ret)
2378     {
2379         /* Seeking is async, so seek_position should not be invalidated here */
2380         return;
2381     }
2382
2383 err:
2384     g_set_error(error,
2385                 WORKER_ERROR,
2386                 WORKER_ERROR_CANNOT_SET_POSITION,
2387                 "Seeking to %d failed", position);
2388     worker->report_statechanges = TRUE;
2389     worker->seek_position = -1;
2390     mafw_gst_renderer_seeker_cancel(worker->seeker);
2391 }
2392
2393 void mafw_gst_renderer_worker_set_current_frame_on_pause(
2394     MafwGstRendererWorker *worker,
2395     gboolean current_frame_on_pause)
2396 {
2397
2398     worker->current_frame_on_pause = current_frame_on_pause;
2399
2400     _emit_property(worker,
2401                    WORKER_PROPERTY_CURRENT_FRAME_ON_PAUSE,
2402                    G_TYPE_BOOLEAN,
2403                    &worker->current_frame_on_pause);
2404 }
2405
2406 gboolean mafw_gst_renderer_worker_get_current_frame_on_pause(
2407     MafwGstRendererWorker *worker)
2408 {
2409     return worker->current_frame_on_pause;
2410 }
2411
2412 configuration *mafw_gst_renderer_worker_create_default_configuration(MafwGstRendererWorker *worker)
2413 {
2414     UNUSED(worker);
2415     return _create_default_configuration();
2416 }
2417
2418 void mafw_gst_renderer_worker_set_configuration(MafwGstRendererWorker *worker,
2419                                                 configuration *config)
2420 {
2421     if( config == NULL )
2422     {
2423         g_warning("NULL config was tried to be set!");
2424         return;
2425     }
2426
2427     if( worker->config )
2428     {
2429         _free_configuration(worker->config);
2430     }
2431     worker->config = config;
2432
2433     if( (worker->pipeline == NULL)
2434         || (worker->state == GST_STATE_NULL && gst_element_get_state(worker->pipeline, NULL, NULL, 0) == GST_STATE_CHANGE_SUCCESS) )
2435     {
2436         _reset_pipeline_and_worker(worker);
2437         _construct_pipeline(worker, worker->config);
2438     }
2439 }
2440
2441 /*
2442  * Sets the pipeline PAUSED-to-READY timeout to given value (in seconds). If the
2443  * pipeline is already in PAUSED state and this called with zero value the pipeline
2444  * get immediately set to READY state.
2445  */
2446 void mafw_gst_renderer_worker_set_ready_timeout(MafwGstRendererWorker *worker,
2447                                                 guint seconds)
2448 {
2449     g_debug(G_STRFUNC);
2450
2451     worker->config->seconds_to_pause_to_ready = seconds;
2452
2453     /* zero is a special case: if we are already in PAUSED state, a pending
2454      * ready timeout has not yet elapsed and we are asked to set the timeout
2455      * value to zero --> remove the pending tmo and go immediately to READY.
2456      * This forces GStreamer to release all pipeline resources and for the
2457      * outsiders it looks like we are still in the PAUSED state. */
2458     if (seconds == 0 && worker->ready_timeout && worker->state == GST_STATE_PAUSED)
2459     {
2460         _remove_ready_timeout(worker);
2461         _add_ready_timeout(worker);
2462     }
2463
2464 }
2465
2466 void mafw_gst_renderer_worker_set_position(MafwGstRendererWorker *worker,
2467                                            GstSeekType seek_type,
2468                                            gint position, GError **error)
2469 {
2470     _do_seek(worker, seek_type, position, TRUE, error);
2471     if (worker->notify_seek_handler)
2472         worker->notify_seek_handler(worker, worker->owner);
2473 }
2474
2475 static gint64 _get_duration(MafwGstRendererWorker *worker)
2476 {
2477     gint64 value = DURATION_UNQUERIED;
2478     GstFormat format = GST_FORMAT_TIME;
2479
2480     gboolean right_query = gst_element_query_duration(worker->pipeline, &format, &value);
2481     if( !right_query )
2482     {
2483         /* just in case gstreamer messes with the value */
2484         value = DURATION_UNQUERIED;
2485     }
2486     return value;
2487 }
2488
2489 /*
2490  * Gets current position, rounded down into precision of one second.  If a seek
2491  * is pending, returns the position we are going to seek.  Returns -1 on
2492  * failure.
2493  */
2494 gint mafw_gst_renderer_worker_get_position(MafwGstRendererWorker *worker)
2495 {
2496     GstFormat format;
2497     gint64 time = 0;
2498     g_assert(worker != NULL);
2499
2500     /* If seek is ongoing, return the position where we are seeking. */
2501     if (worker->seek_position != -1)
2502     {
2503         return worker->seek_position;
2504     }
2505
2506     /* Otherwise query position from pipeline. */
2507     format = GST_FORMAT_TIME;
2508     if (worker->pipeline &&
2509         gst_element_query_position(worker->pipeline, &format, &time))
2510     {
2511         return (gint)(time / GST_SECOND);
2512     }
2513     /* lets return the duration if we're in eos and the pipeline cannot return position */
2514     else if( worker->pipeline && worker->eos )
2515     {
2516         gint64 duration = _get_duration(worker);
2517         if( duration > 0 )
2518         {
2519             return (gint)(duration / GST_SECOND);
2520         }
2521     }
2522     return -1;
2523 }
2524
2525 /*
2526  * Returns the duration of the current media in seconds
2527  */
2528 gint64 mafw_gst_renderer_worker_get_duration(MafwGstRendererWorker *worker)
2529 {
2530     gint64 duration = _get_duration(worker);
2531     if( duration >= 0 )
2532     {
2533         gint64 second_precision = (duration + (GST_SECOND/2)) / GST_SECOND;
2534
2535         if( !_seconds_duration_equal(duration, worker->media.length_nanos) )
2536         {            
2537             worker->media.length_nanos = duration;
2538
2539             if( _current_metadata_add(worker,
2540                                       WORKER_METADATA_KEY_DURATION,
2541                                       G_TYPE_INT64,
2542                                       (const gpointer)&second_precision) )
2543             {
2544                 _emit_metadata(worker,
2545                                WORKER_METADATA_KEY_DURATION,
2546                                G_TYPE_INT64,
2547                                &second_precision);
2548             }
2549         }
2550         return second_precision;
2551     }
2552     else
2553     {
2554         return -1;
2555     }
2556 }
2557
2558 gint64 mafw_gst_renderer_worker_get_last_known_duration(MafwGstRendererWorker *worker)
2559 {
2560     if( worker->media.length_nanos <= 0 )
2561     {
2562         return worker->media.length_nanos;
2563     }
2564     else
2565     {
2566         return (worker->media.length_nanos + (GST_SECOND/2)) / GST_SECOND;
2567     }
2568 }
2569
2570 GHashTable *mafw_gst_renderer_worker_get_current_metadata(
2571     MafwGstRendererWorker *worker)
2572 {
2573     return worker->current_metadata;
2574 }
2575
2576 void mafw_gst_renderer_worker_set_xid(MafwGstRendererWorker *worker, XID xid)
2577 {
2578     /* Store the target window id */
2579     g_debug("Setting xid: %x", (guint)xid);
2580     worker->xid = xid;
2581
2582     if( !worker->vsink )
2583     {
2584         g_debug("Creating video-sink as XID has been set, %s", worker->config->vsink);
2585         worker->vsink = gst_element_factory_make(worker->config->vsink, NULL);
2586         if (!worker->vsink) {
2587             worker->vsink = gst_element_factory_make("xvimagesink", NULL);
2588         }
2589         if (!worker->vsink) {
2590             g_critical("Failed to create pipeline video sink");
2591             _send_error(worker,
2592                         g_error_new(WORKER_ERROR,
2593                                     WORKER_ERROR_UNABLE_TO_PERFORM,
2594                                     "Could not create video sink"));
2595             g_assert_not_reached();
2596         }
2597         gst_object_ref_sink(worker->vsink);
2598
2599         //special case for xvimagesink
2600         {
2601             gchar* element_name = gst_element_get_name(worker->vsink);
2602             g_object_set(G_OBJECT(worker->vsink),
2603                          "colorkey", 0x080810,
2604                          NULL);
2605             if (g_str_has_prefix(element_name, "xvimagesink"))
2606             {
2607                 g_object_set(G_OBJECT(worker->vsink),
2608                              "handle-events", TRUE,
2609                              "force-aspect-ratio", TRUE,
2610                              NULL);
2611             }
2612             g_free(element_name);
2613         }
2614
2615         //do not dare to set video-sink in any other state
2616         if( worker->pipeline && worker->state == GST_STATE_NULL )
2617         {
2618              g_object_set(worker->pipeline,
2619                           "video-sink", worker->vsink,
2620                           NULL);
2621         }
2622     }
2623
2624     /* We don't want to set XID to video sink here when in READY state, because
2625        it prevents "prepare-xwindow-id" message. Setting it when we are
2626        PAUSED or PLAYING is fine, because we already got "prepare-xwindow-id". */
2627     if(worker->state == GST_STATE_PLAYING ||
2628        worker->state == GST_STATE_PAUSED)
2629     {
2630         /* Check if we should use it right away */
2631         mafw_gst_renderer_worker_apply_xid(worker);
2632     }
2633
2634     _emit_property(worker, WORKER_PROPERTY_XID, G_TYPE_UINT, &worker->xid);
2635
2636 }
2637
2638 XID mafw_gst_renderer_worker_get_xid(MafwGstRendererWorker *worker)
2639 {
2640     return worker->xid;
2641 }
2642
2643 void mafw_gst_renderer_worker_set_render_rectangle(MafwGstRendererWorker *worker, render_rectangle *rect)
2644 {
2645     /* Store the target window id */
2646     g_debug("Setting render rectangle: X:%d,Y:%d  Width:%d, Height:%d",
2647             rect->x, rect->y, rect->width, rect->height);
2648
2649     worker->x_overlay_rectangle.x = rect->x;
2650     worker->x_overlay_rectangle.y = rect->y;
2651     worker->x_overlay_rectangle.width = rect->width;
2652     worker->x_overlay_rectangle.height = rect->height;
2653
2654     /* Check if we should use it right away */
2655     mafw_gst_renderer_worker_apply_render_rectangle(worker);
2656
2657     GValueArray *rect_array = g_value_array_new(4);
2658     GValue x;
2659     GValue y;
2660     GValue width;
2661     GValue height;
2662
2663     _set_value(&x, G_TYPE_INT, &(rect->x));
2664     _set_value(&y, G_TYPE_INT, &(rect->y));
2665     _set_value(&width, G_TYPE_INT, &(rect->width));
2666     _set_value(&height, G_TYPE_INT, &(rect->height));
2667
2668     g_value_array_insert(rect_array, 0, &x );
2669     g_value_array_insert(rect_array, 1, &y );
2670     g_value_array_insert(rect_array, 2, &width );
2671     g_value_array_insert(rect_array, 3, &height );
2672
2673     GValue value;
2674     memset(&value, 0, sizeof(value));
2675     g_value_init(&value, G_TYPE_VALUE_ARRAY);
2676     g_value_take_boxed(&value, rect_array);
2677
2678     _emit_property(worker, WORKER_PROPERTY_RENDER_RECTANGLE, G_TYPE_VALUE_ARRAY, &value);
2679
2680     g_value_unset(&value);
2681 }
2682
2683 const render_rectangle* mafw_gst_renderer_worker_get_render_rectangle(MafwGstRendererWorker *worker)
2684 {
2685     UNUSED(worker);
2686     return &worker->x_overlay_rectangle;
2687 }
2688
2689 gboolean mafw_gst_renderer_worker_get_autopaint(MafwGstRendererWorker *worker)
2690 {
2691     return worker->autopaint;
2692 }
2693
2694 void mafw_gst_renderer_worker_set_autopaint(MafwGstRendererWorker *worker,
2695                                             gboolean autopaint)
2696 {
2697     /* TODO Is this a bug or a feature? */
2698     worker->autopaint = autopaint;
2699     if (worker->vsink)
2700         g_object_set(worker->vsink, "autopaint-colorkey", autopaint, NULL);
2701
2702     _emit_property(worker,
2703                    WORKER_PROPERTY_AUTOPAINT,
2704                    G_TYPE_BOOLEAN,
2705                    &autopaint);
2706
2707 }
2708
2709 gboolean mafw_gst_renderer_worker_set_playback_speed(MafwGstRendererWorker* worker,
2710                                                      gfloat speed)
2711 {
2712     gboolean retVal = FALSE;
2713
2714     if (worker->state == GST_STATE_PLAYING)
2715     {
2716         worker->playback_speed = speed;
2717
2718         gint64 current_position;
2719         GstFormat format = GST_FORMAT_TIME;
2720
2721         if (worker->pipeline && gst_element_query_position(worker->pipeline,
2722                                                            &format,
2723                                                            &current_position))
2724         {
2725
2726             retVal = gst_element_seek(worker->pipeline,
2727                                       speed,
2728                                       GST_FORMAT_DEFAULT,
2729                                       GST_SEEK_FLAG_SKIP | GST_SEEK_FLAG_KEY_UNIT,
2730                                       GST_SEEK_TYPE_NONE,
2731                                       current_position,
2732                                       GST_SEEK_TYPE_NONE,
2733                                       GST_CLOCK_TIME_NONE);
2734
2735             if(retVal)
2736             {
2737                 _emit_property(worker,
2738                                WORKER_PROPERTY_PLAYBACK_SPEED,
2739                                G_TYPE_FLOAT,
2740                                &speed);
2741             }
2742         }
2743     }
2744
2745     return retVal;
2746 }
2747
2748 gfloat mafw_gst_renderer_worker_get_playback_speed(MafwGstRendererWorker* worker)
2749 {
2750     return worker->playback_speed;
2751 }
2752
2753 void mafw_gst_renderer_worker_set_force_aspect_ratio(MafwGstRendererWorker *worker, gboolean force)
2754 {
2755
2756     worker->force_aspect_ratio = force;
2757     if (worker->vsink)
2758     {
2759         g_object_set(worker->vsink, "force-aspect-ratio", force, NULL);
2760     }
2761     _emit_property(worker, WORKER_PROPERTY_FORCE_ASPECT_RATIO, G_TYPE_BOOLEAN, &force);
2762
2763 }
2764
2765 gboolean mafw_gst_renderer_worker_get_force_aspect_ratio(MafwGstRendererWorker *worker)
2766 {
2767     return worker->force_aspect_ratio;
2768 }
2769
2770 gint mafw_gst_renderer_worker_get_colorkey(MafwGstRendererWorker *worker)
2771 {
2772     return worker->colorkey;
2773 }
2774
2775 gboolean mafw_gst_renderer_worker_get_seekable(MafwGstRendererWorker *worker)
2776 {
2777     return worker->media.seekable == SEEKABILITY_SEEKABLE;
2778 }
2779
2780 gboolean mafw_gst_renderer_worker_get_streaming(MafwGstRendererWorker *worker)
2781 {
2782     return uri_is_stream(worker->media.location);
2783 }
2784
2785 const char* mafw_gst_renderer_worker_get_uri(MafwGstRendererWorker *worker)
2786 {
2787     return worker->media.location;
2788 }
2789
2790 static void _do_play(MafwGstRendererWorker *worker)
2791 {
2792     g_assert(worker != NULL);
2793
2794     if (worker->pipeline == NULL) {
2795         g_debug("play without a pipeline!");
2796         return;
2797     }
2798     worker->report_statechanges = TRUE;
2799
2800     /* If we have to stay paused, we do and add the ready timeout. Otherwise, we
2801      * move the pipeline */
2802     if (!worker->stay_paused) {
2803         /* If pipeline is READY, we move it to PAUSED, otherwise, to PLAYING */
2804         if (worker->state == GST_STATE_READY) {
2805             gst_element_set_state(worker->pipeline, GST_STATE_PAUSED);
2806             g_debug("setting pipeline to PAUSED");
2807         } else {
2808             gst_element_set_state(worker->pipeline, GST_STATE_PLAYING);
2809             g_debug("setting pipeline to PLAYING");
2810         }
2811     }
2812     else {
2813         g_debug("staying in PAUSED state");
2814         _add_ready_timeout(worker);
2815     }
2816 }
2817
2818 void mafw_gst_renderer_worker_play(MafwGstRendererWorker *worker,
2819                                    const gchar *uri)
2820 {
2821     g_assert(uri);
2822
2823     mafw_gst_renderer_worker_stop(worker);
2824     _reset_media_info(worker);
2825
2826     /* Set the item to be played */
2827     worker->media.location = g_strdup(uri);
2828
2829     _start_play(worker);
2830 }
2831
2832 /*
2833  * Currently, stop destroys the Gst pipeline and resets the worker into
2834  * default startup configuration.
2835  */
2836 void mafw_gst_renderer_worker_stop(MafwGstRendererWorker *worker)
2837 {
2838     g_debug("worker stop");
2839     g_assert(worker != NULL);
2840
2841     /* If location is NULL, this is a pre-created pipeline */
2842     if (worker->async_bus_id && worker->pipeline && !worker->media.location)
2843         return;
2844
2845     _reset_pipeline_and_worker(worker);
2846
2847     /* context framework adaptation starts */
2848     if (worker->context_nowplaying) {
2849         context_provider_map_free(worker->context_nowplaying);
2850         worker->context_nowplaying = NULL;
2851     }
2852     context_provider_set_null(CONTEXT_PROVIDER_KEY_NOWPLAYING);
2853     /* context framework adaptation ends */
2854
2855     /* We are not playing, so we can let the screen blank */
2856     if (worker->blanking__control_handler)
2857     {
2858         worker->blanking__control_handler(worker, worker->owner, FALSE);
2859     }
2860
2861     /* And now get a fresh pipeline ready */
2862     _construct_pipeline(worker, worker->config);
2863 }
2864
2865 void mafw_gst_renderer_worker_pause(MafwGstRendererWorker *worker)
2866 {
2867     g_assert(worker != NULL);
2868
2869     if (worker->buffering && worker->state == GST_STATE_PAUSED &&
2870         !worker->prerolling)
2871     {
2872         /* If we are buffering and get a pause, we have to
2873          * signal state change and stay_paused */
2874         g_debug("Pausing while buffering, signalling state change");
2875
2876         /* We need to make sure that we go into real PAUSE state */
2877         if (worker->blanking__control_handler)
2878         {
2879             worker->blanking__control_handler(worker, worker->owner, FALSE);
2880         }
2881         _do_pause_postprocessing(worker);
2882     }
2883     else
2884     {
2885         worker->report_statechanges = TRUE;
2886         if (worker->seek_position == -1 && worker->state == GST_STATE_PLAYING )
2887         {
2888             gst_element_set_state(worker->pipeline, GST_STATE_PAUSED);
2889             if (worker->blanking__control_handler)
2890             {
2891                 worker->blanking__control_handler(worker, worker->owner, FALSE);
2892             }
2893         }
2894     }
2895
2896     worker->stay_paused = TRUE;
2897     worker->pause_frame_taken = FALSE;
2898 }
2899
2900 /*
2901  * Notifier to call when audio/video routing changes
2902  */
2903 void mafw_gst_renderer_worker_notify_media_destination(
2904     MafwGstRendererWorker *worker,
2905     GSList *destinations)
2906 {
2907     g_assert(worker != NULL);
2908     g_assert(destinations != NULL);
2909
2910     /* 1. update our records of current destinations */
2911     g_slist_free(worker->destinations);
2912     worker->destinations = g_slist_copy(destinations);
2913
2914     /* 2. prevent blanking if we are playing video and outputting it on our own
2915      * display, otherwise disable it */
2916     if (worker->blanking__control_handler &&
2917         worker->media.has_visual_content &&
2918         worker->state == GST_STATE_PLAYING &&
2919         g_slist_find(worker->destinations,
2920                      GINT_TO_POINTER(WORKER_OUTPUT_BUILTIN_DISPLAY)))
2921     {
2922         worker->blanking__control_handler(worker, worker->owner, TRUE);
2923     }
2924     else
2925     {
2926         worker->blanking__control_handler(worker, worker->owner, FALSE);
2927     }
2928
2929     /* 3. disabling Dolby Headphone effect if not outputting to audio jack or
2930      * bluetooth headphones, otherwise using the effect. Actual route check is done
2931      * in set_dolby_*****_property function*/
2932     set_dolby_music_property(worker, worker->config->mobile_surround_music.state);
2933     set_dolby_video_property(worker, worker->config->mobile_surround_video.state);
2934
2935 }
2936
2937 void mafw_gst_renderer_worker_pause_at(MafwGstRendererWorker *worker, guint position)
2938 {
2939     /* the current implementation works only from ready i.e. stopped state */
2940     g_assert( worker != NULL);
2941     worker->stay_paused = TRUE;
2942     worker->pause_frame_taken = FALSE;
2943     worker->seek_position = position;
2944
2945     if( worker->vsink )
2946     {
2947         g_object_set(worker->vsink, "show-preroll-frame",
2948                      FALSE, NULL);
2949     }
2950 }
2951
2952 void mafw_gst_renderer_worker_resume(MafwGstRendererWorker *worker)
2953 {
2954     worker->stay_paused = FALSE;
2955     if (worker->buffering && worker->state == GST_STATE_PAUSED &&
2956         !worker->prerolling) {
2957         /* If we are buffering we cannot resume, but we know that the pipeline
2958          * will be moved to PLAYING as stay_paused is FALSE, so we just
2959          * activate the state change report, this way as soon as buffering
2960          * is finished the pipeline will be set to PLAYING and the state
2961          * change will be reported */
2962         worker->report_statechanges = TRUE;
2963         g_debug("Resumed while buffering, activating pipeline state changes");
2964         /* Notice though that we can receive the Resume before we get any
2965            buffering information. In that case we go with the "else" branch
2966            and set the pipeline to to PLAYING. However, it is possible that
2967            in this case we get the fist buffering signal before the PAUSED ->
2968            PLAYING state change. In that case, since we ignore state changes
2969            while buffering we never signal the state change to PLAYING. We
2970            can only fix this by checking, when we receive a PAUSED -> PLAYING
2971            transition if we are buffering, and in that case signal the state
2972            change (if we get that transition while buffering is on, it can
2973            only mean that the client resumed playback while buffering, and
2974            we must notify the state change) */
2975     } else {
2976         _do_play(worker);
2977     }
2978
2979     /* we want to resume no use for these timers anymore */
2980     _remove_pause_frame_timeout(worker);
2981     _remove_ready_timeout(worker);
2982 }
2983
2984 MafwGstRendererWorker *mafw_gst_renderer_worker_new(gpointer owner)
2985 {
2986     MafwGstRendererWorker *worker;
2987
2988     g_debug("%s", G_STRFUNC);
2989
2990     worker = g_new0(MafwGstRendererWorker, 1);
2991     worker->owner = owner;
2992     worker->report_statechanges = TRUE;
2993     worker->state = GST_STATE_NULL;
2994     worker->seek_position = -1;
2995     worker->ready_timeout = 0;
2996     worker->pause_frame_timeout = 0;
2997     worker->duration_seek_timeout = 0;
2998     worker->duration_seek_timeout_loop_count = 0;
2999     worker->in_ready = FALSE;
3000     worker->xid = 0;
3001     worker->x_overlay_rectangle.x = -1;
3002     worker->x_overlay_rectangle.y = -1;
3003     worker->x_overlay_rectangle.width = -1;
3004     worker->x_overlay_rectangle.height = -1;
3005     worker->autopaint = TRUE;
3006     worker->playback_speed = 1;
3007     worker->colorkey = -1;
3008     worker->vsink = NULL;
3009     worker->asink = NULL;
3010     worker->tsink = NULL;
3011     worker->amixer = NULL;
3012     worker->audiobin = NULL;
3013     worker->tag_list = NULL;
3014     worker->current_metadata = NULL;
3015     worker->media.seekable = SEEKABILITY_SEEKABLE;
3016
3017     worker->destinations = NULL;
3018
3019     worker->current_frame_on_pause = FALSE;
3020     worker->taking_screenshot = FALSE;
3021     worker->force_aspect_ratio = TRUE;
3022     _init_tmp_files_pool(worker);
3023     worker->notify_seek_handler = NULL;
3024     worker->notify_pause_handler = NULL;
3025     worker->notify_play_handler = NULL;
3026     worker->notify_buffer_status_handler = NULL;
3027     worker->notify_eos_handler = NULL;
3028     worker->notify_metadata_handler = NULL;
3029     worker->notify_error_handler = NULL;
3030     worker->blanking__control_handler = NULL;
3031     worker->screenshot_handler = NULL;
3032
3033     worker->config = _create_default_configuration();
3034
3035     worker->seeker = mafw_gst_renderer_seeker_new();
3036
3037     if (!_context_fw_initialised)
3038     {
3039         /* context framework adaptation starts */
3040         if (context_provider_init(DBUS_BUS_SESSION, CONTEXT_PROVIDER_BUS_NAME)) {
3041             _context_fw_initialised = TRUE;
3042             context_provider_install_key(CONTEXT_PROVIDER_KEY_NOWPLAYING, FALSE,
3043                                          NULL, NULL);
3044             g_debug("Initialized context framework provider");
3045         }
3046         else {
3047             g_warning("Could not initialize context framework provider");
3048         }
3049     }
3050     /* context framework adaptation ends */
3051
3052     return worker;
3053
3054 }
3055
3056 void mafw_gst_renderer_worker_exit(MafwGstRendererWorker *worker)
3057 {
3058     _destroy_tmp_files_pool(worker);
3059     _reset_pipeline_and_worker(worker);
3060
3061     /* We are not playing, so we can let the screen blank */
3062     if (worker->blanking__control_handler)
3063     {
3064         worker->blanking__control_handler(worker, worker->owner, FALSE);
3065     }
3066
3067     /* now finally sinks/bins are released */
3068     if( worker->audiobin )
3069     {
3070         gst_object_unref(worker->audiobin);
3071         worker->audiobin = NULL;
3072     }
3073     else if( worker->asink )
3074     {
3075         gst_object_unref(worker->asink);
3076         worker->asink = NULL;
3077     }
3078
3079     if( worker->vsink )
3080     {
3081         gst_object_unref(worker->vsink);
3082         worker->vsink = NULL;
3083     }
3084
3085     context_provider_stop();
3086     _context_fw_initialised = FALSE;
3087
3088     if( worker->destinations )
3089     {
3090         g_slist_free(worker->destinations);
3091         worker->destinations = NULL;
3092     }
3093
3094     if( worker->config )
3095     {
3096         _free_configuration(worker->config);
3097         worker->config = NULL;
3098     }
3099
3100     if( worker->seeker )
3101     {
3102         mafw_gst_renderer_seeker_free(worker->seeker);
3103         worker->seeker = NULL;
3104     }