📄 swfdec_player.c
字号:
/* Swfdec * Copyright (C) 2006-2007 Benjamin Otte <otte@gnome.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA */#ifdef HAVE_CONFIG_H#include "config.h"#endif#include <math.h>#include <stdlib.h>#include <liboil/liboil.h>#include "swfdec_player_internal.h"#include "swfdec_audio_internal.h"#include "swfdec_button_movie.h" /* for mouse cursor */#include "swfdec_cache.h"#include "swfdec_debug.h"#include "swfdec_enums.h"#include "swfdec_event.h"#include "swfdec_js.h"#include "swfdec_listener.h"#include "swfdec_loader_internal.h"#include "swfdec_marshal.h"#include "swfdec_movie.h"#include "swfdec_root_movie.h"#include "swfdec_sprite_movie.h"/*** gtk-doc ***//** * SECTION:SwfdecPlayer * @title: SwfdecPlayer * @short_description: main playback object * * A #SwfdecPlayer is the main object used for playing back Flash files through * Swfdec. * * A player interacts with the outside world in a multitude of ways. The most * important ones are described below. * * Input is handled via the * <link linkend="swfdec-SwfdecLoader">SwfdecLoader</link> class. A * #SwfdecLoader is set on a new player using swfdec_player_set_loader(). * * When the loader has provided enough data, you can start playing the file. * This is done in steps by calling swfdec_player_advance() - preferrably as * often as swfdec_player_get_next_event() indicates. Or you can provide user input * to the player by calling for example swfdec_player_handle_mouse(). * * You can use swfdec_player_render() to draw the current state of the player. * After that, connect to the SwfdecPlayer::invalidate signal to be notified of * changes. * * Audio output is handled via the * <link linkend="swfdec-SwfdecAudio">SwfdecAudio</link> class. One * #SwfdecAudio object is created for every output using the * SwfdecPlayer::audio-added signal. *//** * SwfdecPlayer: * * This is the base object used for playing Flash files. *//** * SECTION:Enumerations * @title: Enumerations * @short_description: enumerations used in Swfdec * * This file lists all of the enumerations used in various parts of Swfdec. *//** * SwfdecMouseCursor: * @SWFDEC_MOUSE_CURSOR_NORMAL: a normal mouse cursor * @SWFDEC_MOUSE_CURSOR_NONE: no mouse image * @SWFDEC_MOUSE_CURSOR_TEXT: a mouse cursor suitable for text editing * @SWFDEC_MOUSE_CURSOR_CLICK: a mouse cursor for clicking a hyperlink or a * button * * This enumeration describes the possible types for the SwfdecPlayer::mouse-cursor * property. *//*** Timeouts ***/static SwfdecTickswfdec_player_get_next_event_time (SwfdecPlayer *player){ if (player->timeouts) { return ((SwfdecTimeout *) player->timeouts->data)->timestamp - player->time; } else { return 0; }}/** * swfdec_player_add_timeout: * @player: a #SwfdecPlayer * @timeout: timeout to add * * Adds a timeout to @player. The timeout will be removed automatically when * triggered, so you need to use swfdec_player_add_timeout() to add it again. * The #SwfdecTimeout struct and callback does not use a data callback pointer. * It's suggested that you use the struct as part of your own bigger struct * and get it back like this: * <programlisting> * typedef struct { * // ... * SwfdecTimeout timeout; * } MyStruct; * * static void * my_struct_timeout_callback (SwfdecTimeout *timeout) * { * MyStruct *mystruct = (MyStruct *) ((void *) timeout - G_STRUCT_OFFSET (MyStruct, timeout)); * * // do stuff * } * </programlisting> **/voidswfdec_player_add_timeout (SwfdecPlayer *player, SwfdecTimeout *timeout){ GList *walk; SwfdecTick next_tick; g_return_if_fail (SWFDEC_IS_PLAYER (player)); g_return_if_fail (timeout != NULL); g_return_if_fail (timeout->timestamp > player->time); g_return_if_fail (timeout->callback != NULL); SWFDEC_LOG ("adding timeout %p", timeout); next_tick = swfdec_player_get_next_event_time (player); /* the order is important, on events with the same time, we make sure the new one is last */ for (walk = player->timeouts; walk; walk = walk->next) { SwfdecTimeout *cur = walk->data; if (cur->timestamp > timeout->timestamp) break; } player->timeouts = g_list_insert_before (player->timeouts, walk, timeout); if (next_tick != swfdec_player_get_next_event_time (player)) g_object_notify (G_OBJECT (player), "next-event");}/** * swfdec_player_remove_timeout: * @player: a #SwfdecPlayer * @timeout: a timeout that should be removed * * Removes the @timeout from the list of scheduled timeouts. THe tiemout must * have been added with swfdec_player_add_timeout() before. **/voidswfdec_player_remove_timeout (SwfdecPlayer *player, SwfdecTimeout *timeout){ SwfdecTick next_tick; g_return_if_fail (SWFDEC_IS_PLAYER (player)); g_return_if_fail (timeout != NULL); g_return_if_fail (timeout->timestamp > player->time); g_return_if_fail (timeout->callback != NULL); SWFDEC_LOG ("removing timeout %p", timeout); next_tick = swfdec_player_get_next_event_time (player); player->timeouts = g_list_remove (player->timeouts, timeout); if (next_tick != swfdec_player_get_next_event_time (player)) g_object_notify (G_OBJECT (player), "next-event");}/*** Actions ***/typedef struct { gpointer object; SwfdecActionFunc func; gpointer data;} SwfdecPlayerAction;/** * swfdec_player_add_action: * @player: a #SwfdecPlayer * @object: object identifying the action * @action_func: function to execute * @action_data: additional data to pass to @func * * Adds an action to the @player. Actions are used by Flash player to solve * reentrancy issues. Instead of calling back into the Actionscript engine, * an action is queued for later execution. So if you're writing code that * is calling Actionscript code, you want to do this by using actions. **/voidswfdec_player_add_action (SwfdecPlayer *player, gpointer object, SwfdecActionFunc action_func, gpointer action_data){ SwfdecPlayerAction *action; g_return_if_fail (SWFDEC_IS_PLAYER (player)); g_return_if_fail (object != NULL); g_return_if_fail (action_func != NULL); SWFDEC_LOG ("adding action %p %p %p", object, action_func, action_data); action = swfdec_ring_buffer_push (player->actions); if (action == NULL) { /* FIXME: limit number of actions to not get inf loops due to scripts? */ swfdec_ring_buffer_set_size (player->actions, swfdec_ring_buffer_get_size (player->actions) + 16); action = swfdec_ring_buffer_push (player->actions); g_assert (action); } action->object = object; action->func = action_func; action->data = action_data;}/** * swfdec_player_remove_all_actions: * @player: a #SwfdecPlayer * @object: object pointer identifying the actions to be removed * * Removes all actions associated with @object. See swfdec_player_add_action() * for details about actions. **/voidswfdec_player_remove_all_actions (SwfdecPlayer *player, gpointer object){ SwfdecPlayerAction *action; guint i; g_return_if_fail (SWFDEC_IS_PLAYER (player)); g_return_if_fail (object != NULL); for (i = 0; i < swfdec_ring_buffer_get_n_elements (player->actions); i++) { action = swfdec_ring_buffer_peek_nth (player->actions, i); if (action->object == object) { SWFDEC_LOG ("removing action %p %p %p", action->object, action->func, action->data); action->object = NULL; } }}static gbooleanswfdec_player_do_action (SwfdecPlayer *player){ SwfdecPlayerAction *action; SwfdecMovie *movie; movie = g_queue_peek_head (player->init_queue); if (movie) { swfdec_movie_run_init (movie); return TRUE; } movie = g_queue_peek_head (player->construct_queue); if (movie) { swfdec_movie_run_construct (movie); return TRUE; } do { action = swfdec_ring_buffer_pop (player->actions); if (action == NULL) return FALSE; } while (action->object == NULL); /* skip removed actions */ action->func (action->object, action->data); SWFDEC_LOG ("executing action %p %p %p", action->object, action->func, action->data); return TRUE;}/*** SwfdecPlayer ***/enum { TRACE, INVALIDATE, ADVANCE, HANDLE_MOUSE, AUDIO_ADDED, AUDIO_REMOVED, LAUNCH, LAST_SIGNAL};static guint signals[LAST_SIGNAL];enum { PROP_0, PROP_CACHE_SIZE, PROP_INITIALIZED, PROP_MOUSE_CURSOR, PROP_NEXT_EVENT, PROP_BACKGROUND_COLOR};G_DEFINE_TYPE (SwfdecPlayer, swfdec_player, G_TYPE_OBJECT)voidswfdec_player_remove_movie (SwfdecPlayer *player, SwfdecMovie *movie){ swfdec_movie_remove (movie); player->movies = g_list_remove (player->movies, movie);}static voidswfdec_player_get_property (GObject *object, guint param_id, GValue *value, GParamSpec * pspec){ SwfdecPlayer *player = SWFDEC_PLAYER (object); switch (param_id) { case PROP_BACKGROUND_COLOR: g_value_set_uint (value, swfdec_player_get_background_color (player)); break; case PROP_CACHE_SIZE: g_value_set_uint (value, player->cache->max_size); break; case PROP_INITIALIZED: g_value_set_boolean (value, swfdec_player_is_initialized (player)); break; case PROP_MOUSE_CURSOR: g_value_set_enum (value, player->mouse_cursor); break; case PROP_NEXT_EVENT: g_value_set_uint (value, swfdec_player_get_next_event (player)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; }}static voidswfdec_player_set_property (GObject *object, guint param_id, const GValue *value, GParamSpec *pspec){ SwfdecPlayer *player = SWFDEC_PLAYER (object); switch (param_id) { case PROP_BACKGROUND_COLOR: swfdec_player_set_background_color (player, g_value_get_uint (value)); break; case PROP_CACHE_SIZE: player->cache->max_size = g_value_get_uint (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; }}static gbooleanfree_registered_class (gpointer key, gpointer value, gpointer playerp){ SwfdecPlayer *player = playerp; g_free (key); JS_RemoveRoot (player->jscx, value); g_free (value); return TRUE;}static voidswfdec_player_dispose (GObject *object){ SwfdecPlayer *player = SWFDEC_PLAYER (object); swfdec_player_stop_all_sounds (player); /* this must happen before we finish the JS player, we have roots in there */ g_hash_table_foreach_steal (player->registered_classes, free_registered_class, player); g_hash_table_destroy (player->registered_classes); while (player->roots) swfdec_movie_destroy (player->roots->data); swfdec_js_finish_player (player); swfdec_player_remove_all_actions (player, player); /* HACK to allow non-removable actions */ g_assert (swfdec_ring_buffer_pop (player->actions) == NULL); swfdec_ring_buffer_free (player->actions); g_assert (player->movies == NULL); g_assert (player->audio == NULL); if (player->rate) { swfdec_player_remove_timeout (player, &player->iterate_timeout); } g_assert (player->timeouts == NULL); g_assert (g_queue_is_empty (player->init_queue)); g_assert (g_queue_is_empty (player->construct_queue)); g_queue_free (player->init_queue); g_queue_free (player->construct_queue); swfdec_cache_unref (player->cache); if (player->loader) { g_object_unref (player->loader); player->loader = NULL; } G_OBJECT_CLASS (swfdec_player_parent_class)->dispose (object);}static voidswfdec_player_update_mouse_cursor (SwfdecPlayer *player){ SwfdecMouseCursor new = SWFDEC_MOUSE_CURSOR_NORMAL; if (!player->mouse_visible) { new = SWFDEC_MOUSE_CURSOR_NONE; } else if (player->mouse_grab != NULL) { /* FIXME: this needs to be more sophisticated, since SwfdecEditText may * want to have different mouse cursors depending on location (it supports * links in theory) */ if (SWFDEC_IS_BUTTON_MOVIE (player->mouse_grab)) new = SWFDEC_MOUSE_CURSOR_CLICK; } if (new != player->mouse_cursor) { player->mouse_cursor = new; g_object_notify (G_OBJECT (player), "mouse-cursor"); }}static voidswfdec_player_update_drag_movie (SwfdecPlayer *player){ double mouse_x, mouse_y; double x, y; SwfdecMovie *movie; if (player->mouse_drag == NULL) return; movie = player->mouse_drag; g_assert (movie->cache_state == SWFDEC_MOVIE_UP_TO_DATE); mouse_x = player->mouse_x; mouse_y = player->mouse_y; swfdec_movie_global_to_local (movie->parent, &mouse_x, &mouse_y); mouse_x = CLAMP (mouse_x, player->mouse_drag_rect.x0, player->mouse_drag_rect.x1); mouse_y = CLAMP (mouse_y, player->mouse_drag_rect.y0, player->mouse_drag_rect.y1); SWFDEC_LOG ("mouse is at %g %g, orighinally (%g %g)", mouse_x, mouse_y, player->mouse_x, player->mouse_y); if (player->mouse_drag_center) { x = (movie->extents.x1 + movie->extents.x0) / 2; y = (movie->extents.y1 + movie->extents.y0) / 2; } else { x = 0; y = 0; } SWFDEC_LOG ("center is at %g %g, mouse is at %g %g", x, y, mouse_x, mouse_y); if (mouse_x - x != movie->matrix.x0 || mouse_y -y != movie->matrix.y0) { movie->matrix.x0 += mouse_x - x; movie->matrix.y0 += mouse_y - y; swfdec_movie_queue_update (movie, SWFDEC_MOVIE_INVALID_MATRIX); }}/** * swfdec_player_set_drag_movie: * @player: a #SwfdecPlayer * @drag: the movie to be dragged by the mouse or NULL to unset * @center: TRUE if the center of @drag should be at the mouse pointer, FALSE if (0,0) * of @drag should be at the mouse pointer. * @rect: NULL or the rectangle that clips the mouse position. The coordinates * are in the coordinate system of the parent of @drag. * * Sets or unsets the movie that is dragged by the mouse. **/voidswfdec_player_set_drag_movie (SwfdecPlayer *player, SwfdecMovie *drag, gboolean center, SwfdecRect *rect){ g_return_if_fail (SWFDEC_IS_PLAYER (player)); g_return_if_fail (drag == NULL || SWFDEC_IS_MOVIE (drag));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -