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