Merge "custom eail widget implementation" into tizen
[platform/core/uifw/eail.git] / eail / eail_video.c
1 /*
2  * Copyright (c) 2013 Samsung Electronics Co., Ltd.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 /**
20  * @file eail_video.c
21  * @brief EailVideo implementation
22  */
23 #include <Elementary.h>
24 #include <Emotion.h>
25 #include <gio/gio.h>
26
27 #include "eail_video.h"
28 #include "eail_utils.h"
29 #include "eail_priv.h"
30
31 static void atk_action_interface_init(AtkActionIface *iface);
32 static void atk_stremable_content_iface_init(AtkStreamableContentIface *iface);
33
34 /**
35  * @brief Define EailVideo type
36  */
37 G_DEFINE_TYPE_WITH_CODE(EailVideo, eail_video, EAIL_TYPE_WIDGET,
38                         G_IMPLEMENT_INTERFACE(ATK_TYPE_ACTION,
39                                               atk_action_interface_init)
40                         G_IMPLEMENT_INTERFACE(ATK_TYPE_STREAMABLE_CONTENT,
41                                               atk_stremable_content_iface_init)
42 );
43
44 /**
45  * @brief Gets a reference to the state set of the accessible
46  *
47  * The caller must unreference it when it is no longer needed.
48  *
49  * Implementation of ref_state_set from AtkObject.
50  *
51  * @param object AtkObject instance
52  *
53  * @returns AtkStateSet representing the state set of the accessible
54  */
55 static AtkStateSet*
56 eail_video_ref_state_set(AtkObject *object)
57 {
58    AtkStateSet *state_set;
59    Evas_Object *widget;
60
61    g_return_val_if_fail(EAIL_IS_VIDEO(object), NULL);
62    widget = eail_widget_get_widget(EAIL_WIDGET(object));
63    if (!widget) return NULL;
64
65    state_set = ATK_OBJECT_CLASS(eail_video_parent_class)->ref_state_set(object);
66    atk_state_set_add_state(state_set, ATK_STATE_ANIMATED);
67
68    return state_set;
69 }
70
71 /**
72  * @brief Gets the accessible name of the accessible
73  *
74  * Implementation of get_name from AtkObject.
75  *
76  * @param object AtkObject instance
77  *
78  * @returns string representing the title of played video file
79  * or NULL if name is not set
80  */
81 static const gchar*
82 eail_video_name_get(AtkObject *object)
83 {
84    Evas_Object *video;
85    const gchar *atk_name;
86
87    g_return_val_if_fail(EAIL_IS_VIDEO(object), NULL);
88
89    atk_name = ATK_OBJECT_CLASS(eail_video_parent_class)->get_name(object);
90    if (atk_name) return atk_name;
91
92    video = eail_widget_get_widget(EAIL_WIDGET(object));
93    if (!video) return NULL;
94
95    return elm_video_title_get(video);
96 }
97
98 /**
99  * @brief Initializes EailVideo object
100  *
101  * @param object AtkObject instance
102  * @param data passed user data
103  */
104 static void
105 eail_video_initialize(AtkObject *object, gpointer data)
106 {
107    ATK_OBJECT_CLASS(eail_video_parent_class)->initialize(object, data);
108
109    object->role = ATK_ROLE_ANIMATION;
110 }
111
112 /**
113  * @brief Initiates EailVideo object
114  *
115  * @param video EailVideo instance
116  */
117 static void
118 eail_video_init(EailVideo *video)
119 {
120    video->forward_desc = NULL;
121    video->next_desc = NULL;
122    video->pause_desc = NULL;
123    video->play_desc = NULL;
124    video->prev_desc = NULL;
125    video->rewind_desc = NULL;
126    video->stop_desc = NULL;
127    video->uri = NULL;
128 }
129
130 /**
131  * @brief Finalizes EailVideo object
132  *
133  * @param object EailVideo instance
134  */
135 static void
136 eail_video_finalize(GObject *object)
137 {
138    EailVideo *video = EAIL_VIDEO(object);
139
140    if (video->forward_desc) free(video->forward_desc);
141    if (video->next_desc) free(video->next_desc);
142    if (video->pause_desc) free(video->pause_desc);
143    if (video->play_desc) free(video->play_desc);
144    if (video->rewind_desc) free(video->rewind_desc);
145    if (video->stop_desc) free(video->stop_desc);
146    if (video->uri) free(video->uri);
147
148    G_OBJECT_CLASS(eail_video_parent_class)->finalize(object);
149 }
150
151 /**
152  * @brief Initializes EailVideo class
153  *
154  * @param klass EailVideoClass instance
155  */
156 static void
157 eail_video_class_init(EailVideoClass *klass)
158 {
159    AtkObjectClass *atk_class = ATK_OBJECT_CLASS(klass);
160    GObjectClass *g_object_class = G_OBJECT_CLASS(klass);
161
162    atk_class->initialize = eail_video_initialize;
163    atk_class->get_name = eail_video_name_get;
164    atk_class->ref_state_set = eail_video_ref_state_set;
165
166    g_object_class->finalize = eail_video_finalize;
167 }
168
169 /**
170  * @brief Gets the name of the specified action of the object
171  *
172  * Implementation of get_action_name from AtkAction interface.
173  *
174  * @param action AtkAction instance
175  * @param i action index
176  *
177  * @returns string representing the name of the specified action
178  */
179 static const gchar*
180 eail_video_action_name_get(AtkAction *action, gint i)
181 {
182    const gchar *action_name;
183    g_return_val_if_fail(EAIL_IS_VIDEO(action), NULL);
184
185    switch (i)
186      {
187        case 0:
188            /*"forward,clicked" - the user clicked the forward button.*/
189            action_name = "forward";
190            break;
191        case 1:
192            /*"next,clicked" - the user clicked the next button.*/
193            action_name = "next";
194            break;
195        case 2:
196            /*"pause,clicked" - the user clicked the pause button.*/
197            action_name = "pause";
198            break;
199        case 3:
200            /*"play,clicked" - the user clicked the play button.*/
201            action_name = "play";
202            break;
203        case 4:
204            /*"prev,clicked" - the user clicked the prev button*/
205            action_name = "prev";
206            break;
207        case 5:
208            /*"rewind,clicked" - the user clicked the rewind button.*/
209            action_name = "rewind";
210            break;
211        case 6:
212            /*"stop,clicked" - the user clicked the stop button.*/
213            action_name = "stop";
214            break;
215        default:
216            action_name = NULL;
217            break;
218      }
219
220    return action_name;
221 }
222
223 /**
224  * @brief Gets the number of accessible actions available on the object
225  *
226  * If there are more than one, the first one is considered
227  * the "default" action of the object.
228  *
229  * Implementation of get_n_actions from AtkAction interface.
230  *
231  * @param action AtkAction instance
232  *
233  * @returns integer representing the number of actions
234  * supported by EailVideo object
235  */
236 static gint
237 eail_video_n_actions_get(AtkAction *action)
238 {
239    return 7;
240 }
241
242 /**
243  * @brief Performs the specified action on the object
244  *
245  * Implementation of do_action from AtkAction interface.
246  *
247  * @param action AtkAction instance
248  * @param i action index
249  *
250  * @returns TRUE on success, FALSE otherwise
251  */
252 static gboolean
253 eail_video_do_action(AtkAction *action, gint i)
254 {
255    Evas_Object *video;
256    gboolean result, playing, seekable;
257    double position, length;
258
259    g_return_val_if_fail(EAIL_IS_VIDEO(action), FALSE);
260
261    video = eail_widget_get_widget(EAIL_WIDGET(action));
262    if (!video) return FALSE;
263    if ((elm_object_disabled_get(video)) || (!evas_object_visible_get(video)))
264      return FALSE;
265
266    playing = elm_video_is_playing_get(video);
267    seekable = elm_video_is_seekable_get(video);
268    length = elm_video_play_length_get(video);
269    position = elm_video_play_position_get(video);
270
271    switch (i)
272      {
273        case 0:
274            if (!seekable)
275              {
276                 result = FALSE;
277                 break;
278              }
279            position += length * 0.3;
280            elm_video_play_position_set(video, position);
281            eail_emit_atk_signal
282                (ATK_OBJECT(action), "visible-data-changed", ATK_TYPE_OBJECT);
283            result = TRUE;
284            break;
285        case 1:
286            if (!seekable)
287              {
288                 result = FALSE;
289                 break;
290              }
291            position += length * 0.1;
292            elm_video_play_position_set(video, position);
293            eail_emit_atk_signal
294                (ATK_OBJECT(action), "visible-data-changed", ATK_TYPE_OBJECT);
295            result = TRUE;
296            break;
297        case 2:
298            if (!playing)
299              {
300                 result = FALSE;
301                 break;
302              }
303            elm_video_pause(video);
304            result = TRUE;
305            break;
306        case 3:
307            if (playing)
308              {
309                 result = FALSE;
310                 break;
311              }
312            elm_video_play(video);
313            eail_emit_atk_signal
314                (ATK_OBJECT(action), "visible-data-changed", ATK_TYPE_OBJECT);
315            result = TRUE;
316            break;
317        case 4:
318            if (!seekable)
319              {
320                 result = FALSE;
321                 break;
322              }
323            position -= length * 0.1;
324            elm_video_play_position_set(video, position);
325            eail_emit_atk_signal
326                (ATK_OBJECT(action), "visible-data-changed", ATK_TYPE_OBJECT);
327            result = TRUE;
328            break;
329        case 5:
330            if (!seekable)
331              {
332                 result = FALSE;
333                 break;
334              }
335            elm_video_play_position_set(video, 0);
336            eail_emit_atk_signal
337                (ATK_OBJECT(action), "visible-data-changed", ATK_TYPE_OBJECT);
338            result = TRUE;
339            break;
340        case 6:
341            elm_video_stop(video);
342            elm_video_play_position_set(video, 0);
343            eail_emit_atk_signal
344                (ATK_OBJECT(action), "visible-data-changed", ATK_TYPE_OBJECT);
345            result = TRUE;
346            break;
347        default:
348            result = FALSE;
349            break;
350
351      }
352
353    return result;
354 }
355
356 /**
357  * @brief Gets the description of the specified action of the object
358  *
359  * Implementation of get_description from AtkAction interface.
360  *
361  * @param action AtkAction instance
362  * @param i action index
363  *
364  * @returns string representing the description of the specified action
365  */
366 static const gchar*
367 eail_video_description_get(AtkAction *action, gint i)
368 {
369    const gchar *action_description;
370    EailVideo *video;
371
372    g_return_val_if_fail(EAIL_IS_VIDEO(action), NULL);
373    video = EAIL_VIDEO(action);
374
375    switch (i)
376      {
377        case 0:
378            action_description = video->forward_desc;
379            break;
380        case 1:
381            action_description = video->next_desc;
382            break;
383        case 2:
384            action_description = video->pause_desc;
385            break;
386        case 3:
387            action_description = video->play_desc;
388            break;
389        case 4:
390            action_description = video->prev_desc;
391            break;
392        case 5:
393            action_description = video->rewind_desc;
394            break;
395        case 6:
396            action_description = video->stop_desc;
397            break;
398        default:
399            action_description = NULL;
400            break;
401      }
402
403    return action_description;
404 }
405
406 /**
407  * @brief Sets a description of the specified action of the object
408  *
409  * Implementation of set_description from AtkAction interface.
410  *
411  * @param action AtkAction instance
412  * @param i action index
413  * @param description action descritpion
414  *
415  * @returns TRUE on success, FALSE otherwise
416  */
417 static gboolean
418 eail_video_description_set(AtkAction *action, gint i, const char *description)
419 {
420    EailVideo *video;
421    gchar **value;
422
423    g_return_val_if_fail(EAIL_IS_VIDEO(action), FALSE);
424    video = EAIL_VIDEO(action);
425    switch (i)
426      {
427        case 0:
428            value = &video->forward_desc;
429            break;
430        case 1:
431            value = &video->next_desc;
432            break;
433        case 2:
434            value = &video->pause_desc;
435            break;
436        case 3:
437            value = &video->play_desc;
438            break;
439        case 4:
440            value = &video->prev_desc;
441            break;
442        case 5:
443            value = &video->rewind_desc;
444            break;
445        case 6:
446            value = &video->stop_desc;
447            break;
448        default:
449            value = NULL;
450            break;
451      }
452    if (value)
453      {
454         free(*value);
455         *value = g_strdup(description);
456         return TRUE;
457      }
458    return FALSE;
459 }
460
461 /**
462  * @brief AtkAction interface initializer
463  *
464  * Function called upon instance creation. It initializes AtkAction
465  * interface implementation i.e hooks method pointers in the interface structure
466  * to the implementing class's implementation.
467  *
468  * @param iface AtkActionIface instance
469  */
470 static void
471 atk_action_interface_init(AtkActionIface *iface)
472 {
473    g_return_if_fail(iface != NULL);
474
475    iface->do_action = eail_video_do_action;
476    iface->get_n_actions = eail_video_n_actions_get;
477    iface->get_name = eail_video_action_name_get;
478    iface->get_description = eail_video_description_get;
479    iface->set_description = eail_video_description_set;
480 }
481
482 /**
483  * @brief Helper function for matching file extension with a mime type
484  *
485  * @param ext file extension
486  *
487  * @returns string representing the mime type
488  */
489 static const char *
490 _match_mime_type(const char *ext)
491 {
492    if (!strcmp(ext, ".264"))
493      return "video/h261";
494    if (!strcmp(ext, ".3g2"))
495      return "video/3gpp2";
496    if (!strcmp(ext, ".3gp"))
497      return "video/3gpp";
498    if (!strcmp(ext, ".asf"))
499      return "video/x-ms-asf";
500    if (!strcmp(ext, ".avi"))
501      return "video/x-msvideo";
502    if (!strcmp(ext, ".clp"))
503      return "application/x-msclip";
504    if (!strcmp(ext, "flv"))
505      return "video/x-flv";
506    if (!strcmp(ext, ".m4v"))
507      return "video/x-m4v";
508    if (!strcmp(ext, ".mkv"))
509      return "video/x-matroska";
510    if (!strcmp(ext, ".mov"))
511      return "video/quicktime";
512    if (!strcmp(ext, ".mp2"))
513      return "audio/mpeg";
514    if (!strcmp(ext, ".mp4"))
515      return "video/mp4";
516    if (!strcmp(ext, ".mpe"))
517      return "video/mpeg";
518    if (!strcmp(ext, ".mpeg"))
519      return "video/mpeg";
520    if (!strcmp(ext, ".mpg"))
521      return "video/mpeg";
522    if (!strcmp(ext, ".mts"))
523      return "model/vnd.mts";
524    if (!strcmp(ext, ".mxf"))
525      return "application/mxf";
526    if (!strcmp(ext, ".ogg"))
527      return "audio/ogg";
528    if (!strcmp(ext, ".ogv"))
529      return "video/ogg";
530    if (!strcmp(ext, ".rm"))
531      return "audio/x-pn-realaudio";
532    if (!strcmp(ext, ".swf"))
533      return "application/x-shockwave-flash";
534    if (!strcmp(ext, ".ts"))
535      return "video/MP2T";
536    if (!strcmp(ext, ".weba"))
537      return "audio/webm";
538    if (!strcmp(ext, ".webm"))
539      return "video/webm";
540    if (!strcmp(ext, ".wmv"))
541      return "video/x-ms-wmv";
542
543    return NULL;
544 }
545
546 /**
547  * @brief Helper function for getting extension from a filename
548  *
549  * @param filename target filename
550  *
551  * @returns string representing the file extension
552  */
553 static const char *
554 _get_file_ext(const char *filename) {
555      const char *ext = strrchr(filename, '.');
556
557      if(!ext) return "";
558      return ext;
559 }
560
561 /**
562  * @brief Helper function for getting the video's file path
563  *
564  * @param widget Evas_Object instance
565  *
566  * @returns string representing the path to video file
567  */
568 static const char *
569 _get_video_path(const Evas_Object *widget)
570 {
571    Evas_Object *emotion;
572
573    emotion = elm_video_emotion_get(widget);
574
575    return emotion_object_file_get(emotion);
576 }
577
578 /**
579  * @brief Gets the character string of the specified mime type
580  *
581  * The first mime type is at position 0, the second at position 1, and so on.
582  *
583  * Implementation of get_mime_type from AtkStreamableContent interface.
584  *
585  * @param streamable AtkStreamableContent instance
586  * @param i index of supported mime type
587  *
588  * @returns string representing the specified mime type
589  */
590 static const gchar*
591 eail_video_mime_type_get(AtkStreamableContent *streamable, gint i)
592 {
593    Evas_Object *widget;
594    const char *path, *ext;
595
596    g_return_val_if_fail(EAIL_IS_VIDEO(streamable), NULL);
597    if (i != 0) return NULL;
598
599    widget = eail_widget_get_widget(EAIL_WIDGET(streamable));
600    path = _get_video_path(widget);
601    ext = _get_file_ext(path);
602
603    return _match_mime_type(ext);
604 }
605
606 /**
607  * @brief Gets the number of mime types supported by this object
608  *
609  * For video widget implementation by ATK-EAIL, there will always be maximum one
610  * supported content type at a time for the given file,
611  * so this function will always return 1 or 0.
612  *
613  * Implementation of get_n_mime_types from AtkStreamableContent interface.
614  *
615  * @param streamable AtkStreamableContent instance
616  *
617  * @returns integer representing the number of mime-types for the given
618  * streamable content ('0' in case when no mime-type was matched for content file)
619  */
620 static gint
621 eail_video_n_mime_types_get(AtkStreamableContent *streamable)
622 {
623    const gchar *mime_type = NULL;
624
625    mime_type = eail_video_mime_type_get(streamable, 0);
626    if (mime_type) return 1;
627
628    /* no mime type for content*/
629    return 0;
630 }
631
632 /**
633  * @brief Gets a string representing an URI
634  *
635  * Gets a string representing a URI in IETF standard format
636  * (see http://www.ietf.org/rfc/rfc2396.txt) from which the object's
637  * content may be streamed in the specified mime-type, if one is available.
638  * If mime_type is NULL, the URI for the default (and possibly only)
639  * mime-type is returned.
640  *
641  * Implementation of get_uri from AtkStreamableContent.
642  *
643  * @param streamable AtkStreamableContent instance
644  * @param mime_type requested mime type
645  *
646  * @returns string representing the video file's URI
647  */
648 static const char *
649 eail_video_get_uri(AtkStreamableContent *streamable, const gchar *mime_type)
650 {
651    Evas_Object *widget;
652    EailVideo *video;
653    const char *mime, *path;
654    GFile *file;
655
656    g_return_val_if_fail(EAIL_IS_VIDEO(streamable), NULL);
657
658    widget = eail_widget_get_widget(EAIL_WIDGET(streamable));
659    video = EAIL_VIDEO(streamable);
660    mime =  eail_video_mime_type_get(streamable, 0);
661    if ((mime) && (mime_type) && (strcmp(mime, mime_type))) return NULL;
662
663    path = _get_video_path(widget);
664    file = g_file_new_for_path(path);
665    if (video->uri) free(video->uri);
666    video->uri = g_file_get_uri(file);
667    g_object_unref(file);
668
669    return video->uri;
670 }
671
672 /**
673  * @brief Gets the content in the specified mime type
674  *
675  * Implementation of get_stream from AtkStreamableContent.
676  *
677  * @param streamable AtkStreamableContent instance
678  * @param mime_type requested mime type
679  *
680  * @returns GIOChannel to video file
681  */
682 static GIOChannel *
683 eail_video_get_stream(AtkStreamableContent *streamable,
684                       const gchar *mime_type)
685 {
686    Evas_Object *widget;
687    GError *error;
688    GIOChannel *channel;
689    const char *path, *mime;
690
691    g_return_val_if_fail(EAIL_IS_VIDEO(streamable), NULL);
692
693    widget = eail_widget_get_widget(EAIL_WIDGET(streamable));
694    mime = eail_video_mime_type_get(streamable, 0);
695    if ((mime) && (strcmp(mime, mime_type))) return NULL;
696    path = _get_video_path(widget);
697
698    error = NULL;
699    channel = g_io_channel_new_file(path, "r", &error);
700    if (error)
701      {
702         WRN("cannot open GIOChannel %s", error->message);
703         g_error_free(error);
704      }
705    return channel;
706 }
707
708 /**
709  * @brief Initializes AtkStreamableContent interface
710  *
711  * @param iface EailVideo instance
712  */
713 static void
714 atk_stremable_content_iface_init(AtkStreamableContentIface *iface)
715 {
716    g_return_if_fail(iface != NULL);
717
718    iface->get_n_mime_types = eail_video_n_mime_types_get;
719    iface->get_mime_type = eail_video_mime_type_get;
720    iface->get_stream = eail_video_get_stream;
721    iface->get_uri = eail_video_get_uri;
722 }
723