2 * Copyright (C) 2006-2007 Benjamin Otte <otte@gnome.org>
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.1 of the License, or (at your option) any later version.
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.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301 USA
25 #include "swfdec_loader_internal.h"
26 #include "swfdec_buffer.h"
27 #include "swfdec_debug.h"
28 #include "swfdec_loadertarget.h"
29 #include "swfdec_player_internal.h"
34 * SECTION:SwfdecLoader
35 * @title: SwfdecLoader
36 * @short_description: object used for input
38 * SwfdecLoader is the base class used for input. Since developers normally
39 * need to adapt input to the needs of their application, this class is
40 * provided to be adapted to their needs.
42 * Since Flash files can load new resources while operating, a #SwfdecLoader
43 * can be instructed to load another resource. It's the loader's responsibility
44 * to make sure the player is allowed to access the resource and provide its
47 * For convenience, a #SwfdecLoader for file access is provided by Swfdec.
53 * This is the base class used for providing input. It is abstract, use a
54 * subclass to provide your input.
58 * SwfdecLoaderDataType:
59 * @SWFDEC_LOADER_DATA_UNKNOWN: Unidentified data or data that cannot be
61 * @SWFDEC_LOADER_DATA_SWF: Data describing a normal Flash file.
62 * @SWFDEC_LOADER_DATA_FLV: Data describing a Flash video stream.
63 * @SWFDEC_LOADER_DATA_XML: Data in XML format.
64 * @SWFDEC_LOADER_DATA_TEXT: Textual data.
66 * This type describes the different types of data that can be loaded inside
67 * Swfdec. Swfdec identifies its data streams and you can use the
68 * swfdec_loader_get_data_type() to acquire more information about the data
69 * inside a #SwfdecLoader.
73 * SwfdecLoaderRequest:
74 * @SWFDEC_LOADER_REQUEST_DEFAULT: Use the default method (this most likely is
76 * @SWFDEC_LOADER_REQUEST_GET: Use HTTP get
77 * @SWFDEC_LOADER_REQUEST_POST: Use HTTP post
79 * Describes the moethod to use for requesting a given URL. These methods map
80 * naturally to HTTP methods, since HTTP is the common method for requesting
84 /*** SwfdecLoader ***/
96 G_DEFINE_ABSTRACT_TYPE (SwfdecLoader
, swfdec_loader
, G_TYPE_OBJECT
)
99 swfdec_loader_get_property (GObject
*object
, guint param_id
, GValue
*value
,
102 SwfdecLoader
*loader
= SWFDEC_LOADER (object
);
106 g_value_set_string (value
, loader
->error
);
109 g_value_set_boolean (value
, loader
->state
== SWFDEC_LOADER_STATE_EOF
);
112 g_value_set_enum (value
, loader
->data_type
);
115 g_value_set_ulong (value
, loader
->size
);
118 g_value_set_ulong (value
, swfdec_loader_get_loaded (loader
));
121 g_value_set_boxed (value
, loader
->url
);
124 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, param_id
, pspec
);
130 swfdec_loader_set_property (GObject
*object
, guint param_id
, const GValue
*value
,
133 SwfdecLoader
*loader
= SWFDEC_LOADER (object
);
137 swfdec_loader_error (loader
, g_value_get_string (value
));
140 if (loader
->size
== 0 && g_value_get_ulong (value
) > 0)
141 swfdec_loader_set_size (loader
, g_value_get_ulong (value
));
144 loader
->url
= g_value_dup_boxed (value
);
145 if (loader
->url
== NULL
) {
146 g_warning ("must set a valid URL");
147 loader
->url
= swfdec_url_new ("");
151 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, param_id
, pspec
);
157 swfdec_loader_dispose (GObject
*object
)
159 SwfdecLoader
*loader
= SWFDEC_LOADER (object
);
161 /* targets are supposed to keep a reference around */
162 g_assert (loader
->target
== NULL
);
163 swfdec_buffer_queue_unref (loader
->queue
);
164 swfdec_url_free (loader
->url
);
165 g_free (loader
->error
);
167 G_OBJECT_CLASS (swfdec_loader_parent_class
)->dispose (object
);
171 swfdec_loader_class_init (SwfdecLoaderClass
*klass
)
173 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
175 object_class
->dispose
= swfdec_loader_dispose
;
176 object_class
->get_property
= swfdec_loader_get_property
;
177 object_class
->set_property
= swfdec_loader_set_property
;
179 g_object_class_install_property (object_class
, PROP_ERROR
,
180 g_param_spec_string ("error", "error", "NULL when no error or string describing error",
181 NULL
, G_PARAM_READABLE
));
182 g_object_class_install_property (object_class
, PROP_EOF
,
183 g_param_spec_boolean ("eof", "eof", "TRUE when all data has been handed to the loader",
184 FALSE
, G_PARAM_READABLE
));
185 g_object_class_install_property (object_class
, PROP_DATA_TYPE
,
186 g_param_spec_enum ("data-type", "data type", "the data's type as identified by Swfdec",
187 SWFDEC_TYPE_LOADER_DATA_TYPE
, SWFDEC_LOADER_DATA_UNKNOWN
, G_PARAM_READABLE
));
188 g_object_class_install_property (object_class
, PROP_SIZE
,
189 g_param_spec_ulong ("size", "size", "amount of bytes in loader",
190 0, G_MAXULONG
, 0, G_PARAM_READWRITE
));
191 g_object_class_install_property (object_class
, PROP_LOADED
,
192 g_param_spec_ulong ("loaded", "loaded", "bytes already loaded",
193 0, G_MAXULONG
, 0, G_PARAM_READWRITE
));
194 g_object_class_install_property (object_class
, PROP_URL
,
195 g_param_spec_boxed ("url", "url", "URL for this file",
196 SWFDEC_TYPE_URL
, G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
200 swfdec_loader_init (SwfdecLoader
*loader
)
202 loader
->queue
= swfdec_buffer_queue_new ();
203 loader
->data_type
= SWFDEC_LOADER_DATA_UNKNOWN
;
206 /*** INTERNAL API ***/
209 swfdec_loader_perform_open (gpointer loaderp
, gpointer unused
)
211 SwfdecLoader
*loader
= loaderp
;
213 swfdec_loader_target_open (loader
->target
, loader
);
217 swfdec_loader_perform_eof (gpointer loaderp
, gpointer unused
)
219 SwfdecLoader
*loader
= loaderp
;
221 swfdec_loader_target_eof (loader
->target
, loader
);
225 swfdec_loader_perform_error (gpointer loaderp
, gpointer unused
)
227 SwfdecLoader
*loader
= loaderp
;
229 swfdec_loader_target_error (loader
->target
, loader
);
233 swfdec_loader_perform_push (gpointer loaderp
, gpointer unused
)
235 SwfdecLoader
*loader
= loaderp
;
237 swfdec_loader_target_parse (loader
->target
, loader
);
241 swfdec_loader_load (SwfdecLoader
*loader
, const char *url_string
,
242 SwfdecLoaderRequest request
, const char *data
, gsize data_len
)
245 SwfdecLoaderClass
*klass
;
248 g_return_val_if_fail (SWFDEC_IS_LOADER (loader
), NULL
);
249 g_return_val_if_fail (url_string
!= NULL
, NULL
);
250 g_return_val_if_fail (data
!= NULL
|| data_len
== 0, NULL
);
252 klass
= SWFDEC_LOADER_GET_CLASS (loader
);
253 g_return_val_if_fail (klass
->load
!= NULL
, NULL
);
254 url
= swfdec_url_new_relative (loader
->url
, url_string
);
255 ret
= g_object_new (G_OBJECT_CLASS_TYPE (klass
), "url", url
, NULL
);
256 swfdec_url_free (url
);
257 klass
->load (ret
, loader
, request
, data
, data_len
);
262 swfdec_loader_close (SwfdecLoader
*loader
)
264 SwfdecLoaderClass
*klass
;
266 g_return_if_fail (SWFDEC_IS_LOADER (loader
));
267 klass
= SWFDEC_LOADER_GET_CLASS (loader
);
270 klass
->close (loader
);
271 if (loader
->state
!= SWFDEC_LOADER_STATE_ERROR
)
272 loader
->state
= SWFDEC_LOADER_STATE_CLOSED
;
276 swfdec_loader_set_target (SwfdecLoader
*loader
, SwfdecLoaderTarget
*target
)
278 g_return_if_fail (SWFDEC_IS_LOADER (loader
));
279 g_return_if_fail (target
== NULL
|| SWFDEC_IS_LOADER_TARGET (target
));
281 if (loader
->target
) {
282 swfdec_player_remove_all_external_actions (loader
->player
, loader
);
284 loader
->target
= target
;
286 loader
->player
= swfdec_loader_target_get_player (target
);
287 switch (loader
->state
) {
288 case SWFDEC_LOADER_STATE_NEW
:
290 case SWFDEC_LOADER_STATE_OPEN
:
291 swfdec_player_add_external_action (loader
->player
, loader
,
292 swfdec_loader_perform_open
, NULL
);
294 case SWFDEC_LOADER_STATE_READING
:
295 swfdec_player_add_external_action (loader
->player
, loader
,
296 swfdec_loader_perform_open
, NULL
);
297 swfdec_player_add_external_action (loader
->player
, loader
,
298 swfdec_loader_perform_push
, NULL
);
300 case SWFDEC_LOADER_STATE_EOF
:
301 swfdec_player_add_external_action (loader
->player
, loader
,
302 swfdec_loader_perform_open
, NULL
);
303 swfdec_player_add_external_action (loader
->player
, loader
,
304 swfdec_loader_perform_push
, NULL
);
305 swfdec_player_add_external_action (loader
->player
, loader
,
306 swfdec_loader_perform_eof
, NULL
);
308 case SWFDEC_LOADER_STATE_ERROR
:
309 swfdec_player_add_external_action (loader
->player
, loader
,
310 swfdec_loader_perform_error
, NULL
);
313 g_assert_not_reached ();
317 loader
->player
= NULL
;
324 * swfdec_loader_error:
325 * @loader: a #SwfdecLoader
326 * @error: a string describing the error
328 * Moves the loader in the error state if it wasn't before. A loader that is in
329 * the error state will not process any more data. Also, internal error
330 * handling scripts may be executed.
333 swfdec_loader_error (SwfdecLoader
*loader
, const char *error
)
335 g_return_if_fail (SWFDEC_IS_LOADER (loader
));
336 g_return_if_fail (error
!= NULL
);
339 SWFDEC_ERROR ("another error in loader %p: %s", loader
, error
);
343 SWFDEC_ERROR ("error in loader %p: %s", loader
, error
);
344 loader
->state
= SWFDEC_LOADER_STATE_ERROR
;
345 loader
->error
= g_strdup (error
);
347 swfdec_player_add_external_action (loader
->player
, loader
,
348 swfdec_loader_perform_error
, NULL
);
352 * swfdec_loader_open:
353 * @loader: a #SwfdecLoader
354 * @url: the real URL used for this loader if it has changed (e.g. after HTTP
355 * redirects) or %NULL if it hasn't changed
357 * Call this function when your loader opened the resulting file. For HTTP this
358 * is when having received the headers. You must call this function before
359 * swfdec_laoder_push() can be called.
362 swfdec_loader_open (SwfdecLoader
*loader
, const char *url
)
364 g_return_if_fail (SWFDEC_IS_LOADER (loader
));
365 g_return_if_fail (loader
->state
== SWFDEC_LOADER_STATE_NEW
);
367 loader
->state
= SWFDEC_LOADER_STATE_OPEN
;
369 swfdec_url_free (loader
->url
);
370 loader
->url
= swfdec_url_new (url
);
371 g_object_notify (G_OBJECT (loader
), "url");
374 swfdec_player_add_external_action (loader
->player
, loader
, swfdec_loader_perform_open
, NULL
);
378 * swfdec_loader_push:
379 * @loader: a #SwfdecLoader
380 * @buffer: new data to make available. The loader takes the reference
383 * Makes the data in @buffer available to @loader and processes it. The @loader
387 swfdec_loader_push (SwfdecLoader
*loader
, SwfdecBuffer
*buffer
)
389 g_return_if_fail (SWFDEC_IS_LOADER (loader
));
390 g_return_if_fail (loader
->state
== SWFDEC_LOADER_STATE_OPEN
|| loader
->state
== SWFDEC_LOADER_STATE_READING
);
391 g_return_if_fail (buffer
!= NULL
);
393 swfdec_buffer_queue_push (loader
->queue
, buffer
);
394 g_object_notify (G_OBJECT (loader
), "loaded");
395 loader
->state
= SWFDEC_LOADER_STATE_READING
;
397 swfdec_player_add_external_action (loader
->player
, loader
,
398 swfdec_loader_perform_push
, NULL
);
403 * @loader: a #SwfdecLoader
405 * Indicates to @loader that no more data will follow. The loader must be open.
408 swfdec_loader_eof (SwfdecLoader
*loader
)
410 g_return_if_fail (SWFDEC_IS_LOADER (loader
));
411 g_return_if_fail (loader
->state
== SWFDEC_LOADER_STATE_OPEN
|| loader
->state
== SWFDEC_LOADER_STATE_READING
);
413 if (loader
->size
== 0) {
414 gulong bytes
= swfdec_loader_get_loaded (loader
);
416 swfdec_loader_set_size (loader
, bytes
);
418 g_object_notify (G_OBJECT (loader
), "eof");
419 loader
->state
= SWFDEC_LOADER_STATE_EOF
;
421 swfdec_player_add_external_action (loader
->player
, loader
,
422 swfdec_loader_perform_eof
, NULL
);
426 * swfdec_loader_get_filename:
427 * @loader: a #SwfdecLoader
429 * Gets the suggested filename to use for this loader. This may be of interest
430 * when displaying information about the file that is played back.
432 * Returns: A string in the glib filename encoding that contains the filename
433 * for this loader. g_free() after use.
436 swfdec_loader_get_filename (SwfdecLoader
*loader
)
438 const SwfdecURL
*url
;
439 const char *path
, *ext
;
442 g_return_val_if_fail (SWFDEC_IS_LOADER (loader
), NULL
);
444 url
= swfdec_loader_get_url (loader
);
445 path
= swfdec_url_get_path (url
);
447 char *s
= strrchr (path
, '/');
454 ret
= g_filename_from_utf8 (path
, -1, NULL
, NULL
, NULL
);
456 ret
= g_strdup ("unknown");
458 ext
= swfdec_loader_data_type_get_extension (loader
->data_type
);
460 char *dot
= strrchr (ret
, '.');
462 guint len
= dot
? strlen (dot
) : G_MAXUINT
;
465 real
= g_strdup_printf ("%s.%s", ret
, ext
);
474 * swfdec_loader_get_url:
475 * @loader: a #SwfdecLoader
477 * Gets the url this loader is handling. This is mostly useful for writing
478 * subclasses of #SwfdecLoader.
480 * Returns: a #SwfdecURL describing @loader.
483 swfdec_loader_get_url (SwfdecLoader
*loader
)
485 g_return_val_if_fail (SWFDEC_IS_LOADER (loader
), NULL
);
491 * swfdec_loader_get_data_type:
492 * @loader: a #SwfdecLoader
494 * Queries the type of data this loader provides. The type is determined
495 * automatically by Swfdec.
497 * Returns: the type this data was identified to be in or
498 * #SWFDEC_LOADER_DATA_UNKNOWN if not identified
501 swfdec_loader_get_data_type (SwfdecLoader
*loader
)
503 g_return_val_if_fail (SWFDEC_IS_LOADER (loader
), SWFDEC_LOADER_DATA_UNKNOWN
);
505 return loader
->data_type
;
509 swfdec_loader_set_data_type (SwfdecLoader
*loader
, SwfdecLoaderDataType type
)
511 g_return_if_fail (SWFDEC_IS_LOADER (loader
));
512 g_return_if_fail (loader
->data_type
== SWFDEC_LOADER_DATA_UNKNOWN
);
513 g_return_if_fail (type
!= SWFDEC_LOADER_DATA_UNKNOWN
);
515 loader
->data_type
= type
;
516 g_object_notify (G_OBJECT (loader
), "data-type");
520 * swfdec_loader_set_size:
521 * @loader: a #SwfdecLoader
522 * @size: the amount of bytes in this loader
524 * Sets the size of bytes in this loader. This function may only be called once.
527 swfdec_loader_set_size (SwfdecLoader
*loader
, gulong size
)
529 g_return_if_fail (SWFDEC_IS_LOADER (loader
));
530 g_return_if_fail (loader
->size
== 0);
531 g_return_if_fail (size
> 0);
534 g_object_notify (G_OBJECT (loader
), "size");
538 * swfdec_loader_get_size:
539 * @loader: a #SwfdecLoader
541 * Queries the amount of bytes inside @loader. If the size is unknown, 0 is
544 * Returns: the total number of bytes for this loader or 0 if unknown
547 swfdec_loader_get_size (SwfdecLoader
*loader
)
549 g_return_val_if_fail (SWFDEC_IS_LOADER (loader
), 0);
555 * swfdec_loader_get_loaded:
556 * @loader: a #SwfdecLoader
558 * Gets the amount of bytes that have already been pushed into @loader and are
559 * available to Swfdec.
561 * Returns: Amount of bytes in @loader
564 swfdec_loader_get_loaded (SwfdecLoader
*loader
)
566 g_return_val_if_fail (SWFDEC_IS_LOADER (loader
), 0);
568 return swfdec_buffer_queue_get_depth (loader
->queue
) +
569 swfdec_buffer_queue_get_offset (loader
->queue
);
573 * swfdec_loader_data_type_get_extension:
574 * @type: a #SwfdecLoaderDataType
576 * Queries the extension to be used for data of the given @type.
578 * Returns: the typical extension for this data type or the empty string
579 * if the type has no extension
582 swfdec_loader_data_type_get_extension (SwfdecLoaderDataType type
)
585 case SWFDEC_LOADER_DATA_UNKNOWN
:
587 case SWFDEC_LOADER_DATA_SWF
:
589 case SWFDEC_LOADER_DATA_FLV
:
591 case SWFDEC_LOADER_DATA_XML
:
593 case SWFDEC_LOADER_DATA_TEXT
:
596 g_warning ("unknown data type %u", type
);
601 /*** X-WWW-FORM-URLENCODED ***/
603 /* if speed ever gets an issue, use a 256 byte array instead of strchr */
604 static const char *urlencode_unescaped
="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_.,:/()'";
606 swfdec_urlencode_append_string (GString
*str
, const char *s
)
608 g_assert (s
!= NULL
);
610 if (strchr (urlencode_unescaped
, *s
))
611 g_string_append_c (str
, *s
);
613 g_string_append_c (str
, '+');
615 g_string_append_printf (str
, "%%%02X", (guint
) *s
);
621 swfdec_urldecode_one_string (const char *s
, const char **out
)
623 GString
*ret
= g_string_new ("");
626 if (strchr (urlencode_unescaped
, *s
)) {
627 g_string_append_c (ret
, *s
);
628 } else if (*s
== '+') {
629 g_string_append_c (ret
, ' ');
630 } else if (*s
== '%') {
633 if (*s
>= '0' && *s
<= '9') {
635 } else if (*s
>= 'A' && *s
<= 'F') {
636 byte
= *s
- 'A' + 10;
637 } else if (*s
>= 'a' && *s
<= 'f') {
638 byte
= *s
- 'a' + 10;
640 g_string_free (ret
, TRUE
);
646 if (*s
>= '0' && *s
<= '9') {
648 } else if (*s
>= 'A' && *s
<= 'F') {
649 byte
+= *s
- 'A' + 10;
650 } else if (*s
>= 'a' && *s
<= 'f') {
651 byte
+= *s
- 'a' + 10;
653 g_string_free (ret
, TRUE
);
657 g_assert (byte
< 256);
658 g_string_append_c (ret
, byte
);
665 return g_string_free (ret
, FALSE
);
669 * swfdec_string_append_urlencoded:
671 * @name: name of the property to append
672 * @value: value of property to append or NULL for empty
674 * Appends a name/value pair in encoded as 'application/x-www-form-urlencoded'
678 swfdec_string_append_urlencoded (GString
*str
, const char *name
, const char *value
)
680 g_return_if_fail (str
!= NULL
);
681 g_return_if_fail (name
!= NULL
);
684 g_string_append_c (str
, '&');
685 swfdec_urlencode_append_string (str
, name
);
686 g_string_append_c (str
, '=');
688 swfdec_urlencode_append_string (str
, value
);
692 * swfdec_urldecode_one:
693 * @string: string in 'application/x-www-form-urlencoded' form
694 * @name: pointer that will hold a newly allocated string for the name of the
695 * parsed property or NULL
696 * @value: pointer that will hold a newly allocated string containing the
697 * value of the parsed property or NULL
698 * @end: If not %NULL, on success, pointer to the first byte in @s that was
699 * not parsed. On failure it will point to the byte causing the problem
701 * Tries to parse the given @string into a name/value pair, assuming the string
702 * is in the application/x-www-form-urlencoded format. If the parsing succeeds,
703 * @name and @value will contain the parsed values and %TRUE will be returned.
705 * Returns: %TRUE if parsing the property succeeded, %FALSE otherwise
708 swfdec_urldecode_one (const char *string
, char **name
, char **value
, const char **end
)
710 char *name_str
, *value_str
;
712 g_return_val_if_fail (string
!= NULL
, FALSE
);
714 name_str
= swfdec_urldecode_one_string (string
, &string
);
715 if (name_str
== NULL
)
717 if (*string
!= '=') {
722 value_str
= swfdec_urldecode_one_string (string
, &string
);
723 if (value_str
== NULL
) {