[SLP Merge] 2011/7/14 12:00PM
[framework/uifw/elementary.git] / src / lib / elm_video.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 #ifdef HAVE_EMOTION
5 # include <Emotion.h>
6 #endif
7
8 /**
9  * @defgroup Video Video
10  *
11  * This object take care of setting up an Emotion object to display it's content with the right
12  * aspect ratio. If build with eio, it also remember the last position that was played and start
13  * playing from this position. It expose the basic property of an Emotion and let the more complex
14  * accessible by retrieving the Emotion object. It also needed to link it with a Elementary @ref player.
15  */
16
17 /* TODO: add buffering support to Emotion and display buffering progression in the theme when needed */
18
19 typedef struct _Widget_Data Widget_Data;
20 struct _Widget_Data
21 {
22    Evas_Object *layout;
23    Evas_Object *emotion;
24
25    Ecore_Timer *timer;
26
27    Eina_Bool stop : 1;
28    Eina_Bool remember : 1;
29 };
30
31 #ifdef HAVE_EMOTION
32 static const char *widtype = NULL;
33
34 static const Evas_Smart_Cb_Description _signals[] = {
35   { NULL, NULL }
36 };
37
38 static void _del_hook(Evas_Object *obj);
39 static void _mirrored_set(Evas_Object *obj, Eina_Bool rtl);
40 static void _theme_hook(Evas_Object *obj);
41 static void _sizing_eval(Evas_Object *obj);
42 static void _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info);
43 static void _on_focus_hook(void *data, Evas_Object *obj);
44 static Eina_Bool _event_hook(Evas_Object *obj, Evas_Object *src,
45                              Evas_Callback_Type type, void *event_info);
46
47 static Eina_Bool
48 _event_hook(Evas_Object *obj, Evas_Object *src __UNUSED__, Evas_Callback_Type type, void *event_info)
49 {
50    if (type != EVAS_CALLBACK_KEY_DOWN) return EINA_FALSE;
51    Evas_Event_Key_Down *ev = event_info;
52    Widget_Data *wd = elm_widget_data_get(obj);
53    if (!wd) return EINA_FALSE;
54    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
55    if (elm_widget_disabled_get(obj)) return EINA_FALSE;
56    if ((!strcmp(ev->keyname, "Left")) || (!strcmp(ev->keyname, "KP_Left")))
57      {
58         double current, last;
59
60         current = elm_video_play_position_get(obj);
61         last = elm_video_play_length_get(obj);
62
63         if (current < last)
64           {
65              current += last / 100;
66              elm_video_play_position_set(obj, current);
67           }
68
69         ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
70         return EINA_TRUE;
71      }
72    if ((!strcmp(ev->keyname, "Right")) || (!strcmp(ev->keyname, "KP_Right")))
73      {
74         double current, last;
75
76         current = elm_video_play_position_get(obj);
77         last = elm_video_play_length_get(obj);
78
79         if (current > 0)
80           {
81              current -= last / 100;
82              if (current < 0) current = 0;
83              elm_video_play_position_set(obj, current);
84           }
85
86         ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
87         return EINA_TRUE;
88      }
89    if (!strcmp(ev->keyname, "space"))
90      {
91         if (elm_video_is_playing(obj))
92           elm_video_pause(obj);
93         else
94           elm_video_play(obj);
95         ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
96         return EINA_TRUE;
97      }
98    fprintf(stderr, "keyname: '%s' not handle\n", ev->keyname);
99    return EINA_FALSE;
100 }
101
102 static void
103 _del_hook(Evas_Object *obj)
104 {
105    Widget_Data *wd = elm_widget_data_get(obj);
106
107    if (!wd) return;
108    if (wd->timer) ecore_timer_del(wd->timer);
109    free(wd);
110 }
111
112 static void
113 _on_focus_hook(void *data __UNUSED__, Evas_Object *obj)
114 {
115    Widget_Data *wd = elm_widget_data_get(obj);
116    if (!wd) return;
117    if (elm_widget_focus_get(obj))
118      {
119         edje_object_signal_emit(wd->layout, "elm,action,focus", "elm");
120         evas_object_focus_set(wd->layout, EINA_TRUE);
121      }
122    else
123      {
124         edje_object_signal_emit(wd->layout, "elm,action,unfocus", "elm");
125         evas_object_focus_set(wd->layout, EINA_FALSE);
126      }
127 }
128
129 static void
130 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
131 {
132    Widget_Data *wd = elm_widget_data_get(obj);
133    if (!wd) return;
134    edje_object_mirrored_set(wd->layout, rtl);
135 }
136
137 static void
138 _theme_hook(Evas_Object *obj)
139 {
140    Widget_Data *wd = elm_widget_data_get(obj);
141    if (!wd) return;
142    _elm_widget_mirrored_reload(obj);
143    _mirrored_set(obj, elm_widget_mirrored_get(obj));
144    _elm_theme_object_set(obj, wd->layout, "video", "base", elm_widget_style_get(obj));
145    edje_object_scale_set(wd->layout, elm_widget_scale_get(obj) *
146                          _elm_config->scale);
147    _sizing_eval(obj);
148 }
149
150 static void
151 _sizing_eval(Evas_Object *obj)
152 {
153    Widget_Data *wd = elm_widget_data_get(obj);
154    Evas_Coord minw = -1, minh = -1;
155    Evas_Coord w, h;
156
157    if (!wd) return;
158    evas_object_size_hint_request_get(wd->emotion, &minw, &minh);
159    evas_object_size_hint_aspect_set(wd->emotion, EVAS_ASPECT_CONTROL_BOTH, minw, minh);
160    edje_object_size_min_calc(wd->layout, &w, &h);
161
162    if (w != 0 && h != 0)
163      {
164         minw = w;
165         minh = h;
166      }
167    evas_object_size_hint_aspect_set(obj, EVAS_ASPECT_CONTROL_BOTH, minw, minh);
168 }
169
170 static void
171 _changed_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
172 {
173    _sizing_eval(data);
174 }
175
176 static void
177 _sub_del(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
178 {
179    Widget_Data *wd = elm_widget_data_get(obj);
180
181    if (wd->remember) emotion_object_last_position_save(wd->emotion);
182 }
183
184 static void
185 _open_done(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
186 {
187    Widget_Data *wd = elm_widget_data_get(data);
188
189    edje_object_signal_emit(wd->layout, "elm,video,open", "elm");
190 }
191
192 static void
193 _playback_started(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
194 {
195    Widget_Data *wd = elm_widget_data_get(data);
196
197    edje_object_signal_emit(wd->layout, "elm,video,play", "elm");
198 }
199
200 static void
201 _playback_finished(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
202 {
203    Widget_Data *wd = elm_widget_data_get(data);
204
205    edje_object_signal_emit(wd->layout, "elm,video,end", "elm");
206 }
207
208 static void
209 _update_aspect_ratio(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
210 {
211    _sizing_eval(data);
212 }
213
214 static void
215 _title_change(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
216 {
217    Widget_Data *wd = elm_widget_data_get(data);
218    const char *title;
219
220    title = emotion_object_title_get(wd->emotion);
221    edje_object_part_text_set(wd->layout, "elm,title", title);
222    edje_object_signal_emit(wd->layout, "elm,video,title", "elm");
223 }
224
225 static void
226 _audio_level_change(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
227 {
228    (void) data;
229 }
230
231 static Eina_Bool
232 _suspend_cb(void *data)
233 {
234    Widget_Data *wd = elm_widget_data_get(data);
235    double interval;
236
237    interval = ecore_timer_interval_get(wd->timer);
238    if (interval <= 20)
239      emotion_object_suspend_set(wd->emotion, EMOTION_SLEEP);
240    else if (interval <= 30)
241      emotion_object_suspend_set(wd->emotion, EMOTION_DEEP_SLEEP);
242    else
243      {
244         emotion_object_suspend_set(wd->emotion, EMOTION_HIBERNATE);
245         wd->timer = NULL;
246         return ECORE_CALLBACK_CANCEL;
247      }
248
249    ecore_timer_interval_set(wd->timer, interval + 10);
250    return ECORE_CALLBACK_RENEW;
251 }
252 #endif
253
254 Eina_Bool
255 _elm_video_check(Evas_Object *video)
256 {
257 #ifdef HAVE_EMOTION
258   ELM_CHECK_WIDTYPE(video, widtype) EINA_FALSE;
259   return EINA_TRUE;
260 #else
261   (void) video;
262   return EINA_FALSE;
263 #endif
264 }
265
266 EAPI Evas_Object *
267 elm_video_add(Evas_Object *parent)
268 {
269 #ifdef HAVE_EMOTION
270    Evas_Object *obj;
271    Evas *e;
272    Widget_Data *wd;
273
274    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
275    ELM_SET_WIDTYPE(widtype, "video");
276    elm_widget_type_set(obj, "video");
277    elm_widget_sub_object_add(parent, obj);
278    elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
279    elm_widget_data_set(obj, wd);
280    elm_widget_del_hook_set(obj, _del_hook);
281    elm_widget_theme_hook_set(obj, _theme_hook);
282    elm_widget_can_focus_set(obj, EINA_TRUE);
283    elm_widget_event_hook_set(obj, _event_hook);
284
285    wd->stop = EINA_FALSE;
286    wd->remember = EINA_FALSE;
287
288    wd->layout = edje_object_add(e);
289    _elm_theme_object_set(obj, wd->layout, "video", "base", "default");
290    elm_widget_resize_object_set(obj, wd->layout);
291    elm_widget_sub_object_add(obj, wd->layout);
292    evas_object_show(wd->layout);
293    evas_object_size_hint_weight_set(wd->layout, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
294
295    wd->emotion = emotion_object_add(e);
296    emotion_object_init(wd->emotion, NULL);
297    edje_object_part_swallow(wd->layout, "elm.swallow.video", wd->emotion);
298    elm_widget_sub_object_add(obj, wd->emotion);
299
300    evas_object_smart_callback_add(wd->emotion, "open_done", _open_done, obj);
301    evas_object_smart_callback_add(wd->emotion, "playback_started", _playback_started, obj);
302    evas_object_smart_callback_add(wd->emotion, "playback_finished", _playback_finished, obj);
303    evas_object_smart_callback_add(wd->emotion, "frame_resize", _update_aspect_ratio, obj);
304    evas_object_smart_callback_add(wd->emotion, "title_change", _title_change, obj);
305    evas_object_smart_callback_add(wd->emotion, "audio_level_change", _audio_level_change, obj);
306
307    evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
308    evas_object_event_callback_add(obj, EVAS_CALLBACK_CHANGED_SIZE_HINTS, _changed_size_hints, NULL);
309    evas_object_smart_callbacks_descriptions_set(obj, _signals);
310
311    _mirrored_set(obj, elm_widget_mirrored_get(obj));
312    _sizing_eval(obj);
313
314    wd->timer = ecore_timer_add(20.0, _suspend_cb, obj);
315
316    return obj;
317 #else
318    (void) parent;
319    return NULL;
320 #endif
321 }
322
323 EAPI void
324 elm_video_file_set(Evas_Object *video, const char *filename)
325 {
326 #ifdef HAVE_EMOTION
327    ELM_CHECK_WIDTYPE(video, widtype);
328    Widget_Data *wd = elm_widget_data_get(video);
329
330    if (wd->remember) emotion_object_last_position_save(wd->emotion);
331    wd->stop = EINA_FALSE;
332    emotion_object_file_set(wd->emotion, filename);
333    emotion_object_last_position_load(wd->emotion);
334    edje_object_signal_emit(wd->layout, "elm,video,load", "elm");
335 #else
336    (void) video;
337    (void) filename;
338 #endif
339 }
340
341 EAPI void
342 elm_video_uri_set(Evas_Object *video, const char *uri)
343 {
344 #ifdef HAVE_EMOTION
345    ELM_CHECK_WIDTYPE(video, widtype);
346    Widget_Data *wd = elm_widget_data_get(video);
347
348    if (wd->remember) emotion_object_last_position_save(wd->emotion);
349    wd->stop = EINA_FALSE;
350    emotion_object_file_set(wd->emotion, uri);
351    edje_object_signal_emit(wd->layout, "elm,video,load", "elm");
352 #else
353    (void) video;
354    (void) uri;
355 #endif
356 }
357
358 EAPI Evas_Object *
359 elm_video_emotion_get(Evas_Object *video)
360 {
361 #ifdef HAVE_EMOTION
362    ELM_CHECK_WIDTYPE(video, widtype) NULL;
363    Widget_Data *wd = elm_widget_data_get(video);
364
365    return wd->emotion;
366 #else
367    (void) video;
368    return NULL;
369 #endif
370 }
371
372 EAPI void
373 elm_video_play(Evas_Object *video)
374 {
375 #ifdef HAVE_EMOTION
376    ELM_CHECK_WIDTYPE(video, widtype);
377    Widget_Data *wd = elm_widget_data_get(video);
378
379    if (emotion_object_play_get(wd->emotion)) return ;
380
381    if (wd->timer) ecore_timer_del(wd->timer);
382    wd->timer = NULL;
383    wd->stop = EINA_FALSE;
384    emotion_object_play_set(wd->emotion, EINA_TRUE);
385 #else
386    (void) video;
387 #endif
388 }
389
390 /* FIXME: pause will setup timer and go into sleep or
391  * hibernate after a while without activity.
392  */
393
394 EAPI void
395 elm_video_pause(Evas_Object *video)
396 {
397 #ifdef HAVE_EMOTION
398    ELM_CHECK_WIDTYPE(video, widtype);
399    Widget_Data *wd = elm_widget_data_get(video);
400
401    if (!emotion_object_play_get(wd->emotion)) return ;
402
403    if (!wd->timer) wd->timer = ecore_timer_add(20.0, _suspend_cb, video);
404    emotion_object_play_set(wd->emotion, EINA_FALSE);
405    edje_object_signal_emit(wd->layout, "elm,video,pause", "elm");
406 #else
407    (void) video;
408 #endif
409 }
410
411 /* FIXME: stop should go into hibernate state directly.
412  */
413 EAPI void
414 elm_video_stop(Evas_Object *video)
415 {
416 #ifdef HAVE_EMOTION
417    ELM_CHECK_WIDTYPE(video, widtype);
418    Widget_Data *wd = elm_widget_data_get(video);
419
420    if (!emotion_object_play_get(wd->emotion) && wd->stop) return ;
421
422    if (wd->timer) ecore_timer_del(wd->timer);
423    wd->timer = NULL;
424    wd->stop = EINA_TRUE;
425    emotion_object_play_set(wd->emotion, EINA_FALSE);
426    edje_object_signal_emit(wd->layout, "elm,video,stop", "elm");
427    emotion_object_suspend_set(wd->emotion, EMOTION_HIBERNATE);
428 #else
429    (void) video;
430 #endif
431 }
432
433 EAPI Eina_Bool
434 elm_video_is_playing(Evas_Object *video)
435 {
436 #ifdef HAVE_EMOTION
437    ELM_CHECK_WIDTYPE(video, widtype) EINA_FALSE;
438    Widget_Data *wd = elm_widget_data_get(video);
439
440    return emotion_object_play_get(wd->emotion);
441 #else
442    (void) video;
443    return EINA_FALSE;
444 #endif
445 }
446
447 EAPI Eina_Bool
448 elm_video_is_seekable(Evas_Object *video)
449 {
450 #ifdef HAVE_EMOTION
451    ELM_CHECK_WIDTYPE(video, widtype) EINA_FALSE;
452    Widget_Data *wd = elm_widget_data_get(video);
453
454    return emotion_object_seekable_get(wd->emotion);
455 #else
456    (void) video;
457    return EINA_FALSE;
458 #endif
459 }
460
461 EAPI Eina_Bool
462 elm_video_audio_mute_get(Evas_Object *video)
463 {
464 #ifdef HAVE_EMOTION
465    ELM_CHECK_WIDTYPE(video, widtype) EINA_FALSE;
466    Widget_Data *wd = elm_widget_data_get(video);
467
468    return emotion_object_audio_mute_get(wd->emotion);
469 #else
470    (void) video;
471    return EINA_FALSE;
472 #endif
473 }
474
475 EAPI void
476 elm_video_audio_mute_set(Evas_Object *video, Eina_Bool mute)
477 {
478 #ifdef HAVE_EMOTION
479    ELM_CHECK_WIDTYPE(video, widtype);
480    Widget_Data *wd = elm_widget_data_get(video);
481
482    emotion_object_audio_mute_set(wd->emotion, mute);
483 #else
484    (void) video;
485    (void) mute;
486 #endif
487 }
488
489 EAPI double
490 elm_video_audio_level_get(Evas_Object *video)
491 {
492 #ifdef HAVE_EMOTION
493    ELM_CHECK_WIDTYPE(video, widtype) 0.0;
494    Widget_Data *wd = elm_widget_data_get(video);
495
496    return emotion_object_audio_volume_get(wd->emotion);
497 #else
498    (void) video;
499    return 0.0;
500 #endif
501 }
502
503 EAPI void
504 elm_video_audio_level_set(Evas_Object *video, double volume)
505 {
506 #ifdef HAVE_EMOTION
507    ELM_CHECK_WIDTYPE(video, widtype);
508    Widget_Data *wd = elm_widget_data_get(video);
509
510    emotion_object_audio_volume_set(wd->emotion, volume);
511 #else
512    (void) video;
513    (void) volume;
514 #endif
515 }
516
517 EAPI double
518 elm_video_play_position_get(Evas_Object *video)
519 {
520 #ifdef HAVE_EMOTION
521    ELM_CHECK_WIDTYPE(video, widtype) 0.0;
522    Widget_Data *wd = elm_widget_data_get(video);
523
524    return emotion_object_position_get(wd->emotion);
525 #else
526    (void) video;
527    return 0.0;
528 #endif
529 }
530
531 EAPI void
532 elm_video_play_position_set(Evas_Object *video, double position)
533 {
534 #ifdef HAVE_EMOTION
535    ELM_CHECK_WIDTYPE(video, widtype);
536    Widget_Data *wd = elm_widget_data_get(video);
537
538    emotion_object_position_set(wd->emotion, position);
539 #else
540    (void) video;
541    (void) position;
542 #endif
543 }
544
545 EAPI double
546 elm_video_play_length_get(Evas_Object *video)
547 {
548 #ifdef HAVE_EMOTION
549    ELM_CHECK_WIDTYPE(video, widtype) 0.0;
550    Widget_Data *wd = elm_widget_data_get(video);
551
552    return emotion_object_play_length_get(wd->emotion);
553 #else
554    (void) video;
555    return 0.0;
556 #endif
557 }
558
559 EAPI const char *
560 elm_video_title_get(Evas_Object *video)
561 {
562 #ifdef HAVE_EMOTION
563    ELM_CHECK_WIDTYPE(video, widtype) NULL;
564    Widget_Data *wd = elm_widget_data_get(video);
565
566    return emotion_object_title_get(wd->emotion);
567 #else
568    (void) video;
569    return NULL;
570 #endif
571 }
572
573 EAPI void
574 elm_video_remember_position_set(Evas_Object *video, Eina_Bool remember)
575 {
576 #ifdef HAVE_EMOTION
577    ELM_CHECK_WIDTYPE(video, widtype);
578    Widget_Data *wd = elm_widget_data_get(video);
579
580    wd->remember = remember;
581 #else
582    (void) video;
583    (void) remember;
584 #endif
585 }
586
587 EAPI Eina_Bool
588 elm_video_remember_position_get(Evas_Object *video)
589 {
590 #ifdef HAVE_EMOTION
591    ELM_CHECK_WIDTYPE(video, widtype) EINA_FALSE;
592    Widget_Data *wd = elm_widget_data_get(video);
593
594    return wd->remember;
595 #else
596    (void) video;
597    return EINA_FALSE;
598 #endif
599 }