255 lines
8.6 KiB
Plaintext
255 lines
8.6 KiB
Plaintext
~~Title: Multimedia Tutorial~~
|
||
==== Multimedia Tutorial ====
|
||
|
||
In this tutorial, we will see how to play a multimedia file (video) in an
|
||
application.
|
||
|
||
=== Table of Contents ===
|
||
|
||
* [[#Basic_video_widgets|Basic video widgets]]
|
||
* [[#Getting_more_information|Getting more information]]
|
||
* [[#Playing_Status|Playing Status]]
|
||
* [[#Get_the_file_name_and_location|Get the file name and location]]
|
||
* [[#Get_time_position_and_duration|Get time position and duration]]
|
||
* [[#Get_the_video_dimensions|Get the video dimensions]]
|
||
|
||
Multimedia example: {{ :multimedia.png }}
|
||
//**__The whole code__: **//{{:code_c/tutorial/multimedia/multimedia.c}}
|
||
|
||
\\
|
||
The EFL have a special library for multimedia file playing purposes: Emotion.
|
||
That library has some wrappers in Elementary to let you easily write
|
||
applications: these are ''Elm_Video'' and ''Elm_Player''.
|
||
|
||
* Elm_Video provides a simple video object.
|
||
* Elm_Player provides an interface to show a bar with basic actions (like rewind, fast forward, pause, etc.) to interract with a playing video.
|
||
|
||
-----------
|
||
|
||
=== Basic video widgets ===
|
||
|
||
<code c>
|
||
Evas_Object *video;
|
||
video = elm_video_add(win);
|
||
evas_object_size_hint_weight_set(video, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
|
||
elm_video_file_set(video, FILE);
|
||
elm_video_play(video);
|
||
evas_object_show(video);
|
||
</code>
|
||
|
||
Create a new ''Elm_Video'' object. This object is the main widget for a video.
|
||
The actual video file is then set (which is here the ''FILE'' macro).
|
||
''Elm_Video'' can take either a path to a file or any kind of URL. Finally,
|
||
the video starts playing
|
||
|
||
<code c>
|
||
Evas_Object *player;
|
||
player = elm_player_add(win);
|
||
evas_object_size_hint_weight_set(player, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
|
||
elm_object_content_set(player, video);
|
||
evas_object_smart_callback_add(player, "info,clicked", _player_info_cb, video);
|
||
evas_object_show(player);
|
||
</code>
|
||
|
||
Now create an ''Elm_Player'' object. This object wraps around an ''Elm_Video''
|
||
to have it automatically resized, to show a user interface with basic buttons,
|
||
a progress bar and other stuff. A callback is added for the
|
||
''info,clicked'' event, which is launched when the user clicks on the
|
||
information button in the player interface. This callback will be detailed
|
||
further.
|
||
|
||
The player interface is eventually set as a naviframe item.
|
||
|
||
<code c>
|
||
Elm_Object_Item *it = elm_naviframe_item_push(nav, "Video", NULL, NULL, player, NULL);
|
||
elm_naviframe_item_title_enabled_set(it, EINA_FALSE, EINA_FALSE);
|
||
</code>
|
||
|
||
=== Getting more information ===
|
||
|
||
The purpose is to display some information about the video the user wants to. For
|
||
instance, the video file name, its location, duration, and
|
||
image size will be displayed. This list is of course not exhaustive and much
|
||
more information could be added.
|
||
|
||
Some are not directly available in ''Elm_Video'' nor in
|
||
''Elm_Player''. But the underlaying Emotion object is available with the
|
||
''elm_video_emotion_get(video)'' function.
|
||
|
||
== Playing Status ==
|
||
|
||
Set the label to display the playing status in ''_player_info_cb''.
|
||
|
||
<code c>
|
||
label = elm_label_add(table);
|
||
evas_object_show(label);
|
||
_player_info_status_update(label, emotion, NULL);
|
||
elm_table_pack(table, label, 0, 0, 2, 1);
|
||
evas_object_smart_callback_add(emotion, "playback_finished", _player_info_status_update, label);
|
||
</code>
|
||
|
||
That function is also registered as a callback upon ''playback_finished'' so
|
||
that the status is updated upon playback completion.
|
||
|
||
Get the playing status with ''_player_info_status_update'' callback:
|
||
The ''emotion_object_play_get'' return true if the video is playing, if not
|
||
the video is in paused or ended.
|
||
|
||
<code c>
|
||
static void
|
||
_player_info_status_update(void *data, Evas_Object *obj, void *event_info)
|
||
{
|
||
Evas_Object *emotion = obj, *label = data;
|
||
char buf[256];
|
||
|
||
//switch on main item
|
||
if (!info)
|
||
{
|
||
evas_object_smart_callback_del(obj, "playback_finished", _player_info_status_update);
|
||
return;
|
||
}
|
||
|
||
//update
|
||
double position = emotion_object_position_get(emotion);
|
||
double duration = emotion_object_play_length_get(emotion);
|
||
if (emotion_object_play_get(emotion))
|
||
elm_object_text_set(label, "<b>Playing</b>");
|
||
else if (position < duration)
|
||
elm_object_text_set(label, "<b>Paused</b>");
|
||
else
|
||
elm_object_text_set(label, "<b>Ended</b>");
|
||
}
|
||
</code>
|
||
|
||
== Get the file name and location ==
|
||
|
||
Get the file name and location with ''emotion_object_file_get'' and
|
||
''ecore_file_file_get'' functions.
|
||
|
||
<code c>
|
||
label = elm_label_add(table);
|
||
elm_object_text_set(label, "File:");
|
||
evas_object_show(label);
|
||
elm_table_pack(table, label, 0, 1, 1, 1);
|
||
|
||
const char *file = emotion_object_file_get(emotion);
|
||
label = elm_label_add(table);
|
||
elm_object_text_set(label, ecore_file_file_get(file));
|
||
evas_object_show(label);
|
||
elm_table_pack(table, label, 1, 1, 1, 1);
|
||
|
||
label = elm_label_add(table);
|
||
elm_object_text_set(label, "Location:");
|
||
evas_object_show(label);
|
||
elm_table_pack(table, label, 0, 2, 1, 1);
|
||
|
||
label = elm_label_add(table);
|
||
elm_object_text_set(label, ecore_file_dir_get(file));
|
||
evas_object_show(label);
|
||
elm_table_pack(table, label, 1, 2, 1, 1);
|
||
</code>
|
||
|
||
== Get time position and duration ==
|
||
|
||
Get video time position and duration using ''elm_video_play_position_get'' and
|
||
''elm_video_play_length_get'' functions. These functions returns double time
|
||
values in seconds.
|
||
|
||
<code c>
|
||
label = elm_label_add(table);
|
||
elm_object_text_set(label, "Time:");
|
||
evas_object_show(label);
|
||
elm_table_pack(table, label, 0, 3, 1, 1);
|
||
|
||
label = elm_label_add(table);
|
||
double position = elm_video_play_position_get(video);
|
||
double duration = elm_video_play_length_get(video);
|
||
int p_sec = (int) position % 60;
|
||
int p_min = position / 60;
|
||
int p_hour = position / 3600;
|
||
int d_sec = (int) duration % 60;
|
||
int d_min = duration / 60;
|
||
int d_hour = duration / 3600;
|
||
snprintf(buf, sizeof(buf), "%d:%02d:%02d / %d:%02d:%02d", p_hour, p_min, p_sec, d_hour, d_min, d_sec);
|
||
elm_object_text_set(label, buf);
|
||
evas_object_show(label);
|
||
elm_table_pack(table, label, 1, 3, 1, 1);
|
||
evas_object_smart_callback_add(emotion, "position_update", _player_info_time_update, label);
|
||
evas_object_smart_callback_add(emotion, "length_change", _player_info_time_update, label);
|
||
</code>
|
||
|
||
Here, a callback on both position_update and length_change events are added so
|
||
that timings are always up-to-date.
|
||
|
||
<code c>
|
||
static void
|
||
_player_info_time_update(void *data, Evas_Object *obj, void *event_info)
|
||
{
|
||
Evas_Object *emotion = obj, *label = data;
|
||
char buf[256];
|
||
|
||
//switch on main item
|
||
if (!info)
|
||
{
|
||
evas_object_smart_callback_del(emotion, "position_update", _player_info_time_update);
|
||
evas_object_smart_callback_del(emotion, "length_change", _player_info_time_update);
|
||
return;
|
||
}
|
||
|
||
//update
|
||
double position = emotion_object_position_get(emotion);
|
||
double duration = emotion_object_play_length_get(emotion);
|
||
int p_sec = (int) position % 60;
|
||
int p_min = position / 60;
|
||
int p_hour = position / 3600;
|
||
int d_sec = (int) duration % 60;
|
||
int d_min = duration / 60;
|
||
int d_hour = duration / 3600;
|
||
snprintf(buf, sizeof(buf), "%d:%02d:%02d / %d:%02d:%02d", p_hour, p_min, p_sec, d_hour, d_min, d_sec);
|
||
elm_object_text_set(label, buf);
|
||
}
|
||
</code>
|
||
|
||
In this callback, the emotion functions ''emotion_object_position_get'' and
|
||
''emotion_object_play_length_get'' are used instead of ''elm_video_play_position_get'' and
|
||
''elm_video_play_length_get''. They have the exact same semantics, but are shown
|
||
here so that you know that some data exposed through Emotion may also be
|
||
available thanks to ''Elm_Video'': ''_player_info_status_update'' could also
|
||
have been called directly after the widget creation as for
|
||
''_player_info_time_update''.
|
||
|
||
== Get the video dimensions ==
|
||
|
||
Finally get the video dimensions using ''emotion_object_size_get'' by giving
|
||
width and height pointers as parameters:
|
||
|
||
<code c>
|
||
label = elm_label_add(table);
|
||
elm_object_text_set(label, "Size:");
|
||
evas_object_show(label);
|
||
elm_table_pack(table, label, 0, 4, 1, 1);
|
||
label = elm_label_add(table);
|
||
int w, h;
|
||
emotion_object_size_get(emotion, &w, &h);
|
||
snprintf(buf, sizeof(buf), "%d × %d", w, h);
|
||
elm_object_text_set(label, buf);
|
||
evas_object_show(label);
|
||
elm_table_pack(table, label, 1, 4, 1, 1);
|
||
</code>
|
||
|
||
All of this is shown in a separate naviframe item.
|
||
|
||
<code c>
|
||
Elm_Object_Item *it = elm_naviframe_item_push(nav, "Information", NULL, NULL, table, NULL);
|
||
elm_naviframe_item_pop_cb_set(it, _player_info_del_cb, NULL);
|
||
</code>
|
||
|
||
The ''_player_info_del_cb'' function is here registered in order to be called
|
||
when the naviframe item is popped. This callback sets the global boolean
|
||
''info'' to ''EINA_FALSE'', and so allows other delete callbacks to be
|
||
called.
|
||
|
||
{{ :multimedia_info.png }}
|
||
\\
|
||
//**__The whole code__: **//{{:code_c/tutorial/multimedia/multimedia.c}}
|