add blend mode tests
[swfdec.git] / swfdec / swfdec_stream.c
blobef32bbe78eace2ecd3ad92e0476624fc2bb8007d
1 /* Swfdec
2 * Copyright (C) 2006-2008 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.
8 *
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
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
24 #include <string.h>
25 #include "swfdec_loader_internal.h"
26 #include "swfdec_buffer.h"
27 #include "swfdec_debug.h"
28 #include "swfdec_stream_target.h"
29 #include "swfdec_player_internal.h"
31 /*** gtk-doc ***/
33 /**
34 * SECTION:SwfdecStream
35 * @title: SwfdecStream
36 * @short_description: object used for input
38 * SwfdecStream is the base class used for communication inside Swfdec. If you
39 * are a UNIX developer, think of this class as the equivalent to a file
40 * descriptor. #SwfdecLoader and #SwfdecSocket are the subclasses supposed to
41 * be used for files or network sockets, respectively.
43 * This class provides the functions necessary to implement subclasses of
44 * streams. None of the functions described in this section should be used by
45 * anything but subclass implementations. Consider them "protected".
48 /**
49 * SwfdecStream:
51 * This is the base object used for providing input. It is abstract, use a
52 * subclass to provide your input. All members are considered private.
55 /**
56 * SwfdecStreamClass:
57 * @describe: Provide a string describing your string. Default implementations
58 * of this function exist for both the #SwfdecLoader and
59 * #SwfdecStream subclasses. They return the URL for the stream.
60 * @close: Called when Swfdec requests that the stream be closed. After this
61 * function was called, Swfdec will consider the stream finished and
62 * will not ever read data from it again.
64 * This is the base class used for providing input. You are supposed to create
65 * a subclass that fills in the function pointers mentioned above.
68 /*** SwfdecStream ***/
70 typedef enum {
71 SWFDEC_STREAM_STATE_CONNECTING = 0, /* stream is still in the process of establishing a connection */
72 SWFDEC_STREAM_STATE_OPEN, /* stream is open and data flow is happening */
73 SWFDEC_STREAM_STATE_CLOSED, /* loader has been closed */
74 SWFDEC_STREAM_STATE_ERROR /* loader is in error state */
75 } SwfdecStreamState;
77 struct _SwfdecStreamPrivate
79 SwfdecPlayer * player; /* player to queue target notificaions in */
80 SwfdecStreamTarget * target; /* SwfdecStreamTarget that gets notified about loading progress */
81 SwfdecStreamState state; /* SwfdecStreamState the stream is currently in */
82 SwfdecStreamState processed_state;/* SwfdecStreamState the target knows about */
83 gboolean queued; /* TRUE if we have queued an action already */
84 char * error; /* error message if in error state or NULL */
85 SwfdecBufferQueue * queue; /* SwfdecBufferQueue managing the input buffers */
88 enum {
89 PROP_0,
90 PROP_COMPLETE,
91 PROP_ERROR,
92 PROP_OPEN,
95 G_DEFINE_ABSTRACT_TYPE (SwfdecStream, swfdec_stream, G_TYPE_OBJECT)
97 static void
98 swfdec_stream_get_property (GObject *object, guint param_id, GValue *value,
99 GParamSpec * pspec)
101 SwfdecStreamPrivate *stream = SWFDEC_STREAM (object)->priv;
103 switch (param_id) {
104 case PROP_ERROR:
105 g_value_set_string (value, stream->error);
106 break;
107 case PROP_OPEN:
108 g_value_set_boolean (value, stream->state == SWFDEC_STREAM_STATE_OPEN);
109 break;
110 case PROP_COMPLETE:
111 g_value_set_boolean (value, stream->state == SWFDEC_STREAM_STATE_CLOSED);
112 break;
113 default:
114 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
115 break;
119 static void
120 swfdec_stream_set_property (GObject *object, guint param_id, const GValue *value,
121 GParamSpec *pspec)
123 //SwfdecStream *stream = SWFDEC_STREAM (object);
125 switch (param_id) {
126 default:
127 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
128 break;
132 static void
133 swfdec_stream_dispose (GObject *object)
135 SwfdecStreamPrivate *stream = SWFDEC_STREAM (object)->priv;
137 /* targets are supposed to keep a reference around */
138 g_assert (stream->target == NULL);
139 if (stream->queue) {
140 swfdec_buffer_queue_unref (stream->queue);
141 stream->queue = NULL;
143 g_free (stream->error);
144 stream->error = NULL;
146 G_OBJECT_CLASS (swfdec_stream_parent_class)->dispose (object);
149 static void
150 swfdec_stream_class_init (SwfdecStreamClass *klass)
152 GObjectClass *object_class = G_OBJECT_CLASS (klass);
154 g_type_class_add_private (klass, sizeof (SwfdecStreamPrivate));
156 object_class->dispose = swfdec_stream_dispose;
157 object_class->get_property = swfdec_stream_get_property;
158 object_class->set_property = swfdec_stream_set_property;
160 g_object_class_install_property (object_class, PROP_ERROR,
161 g_param_spec_string ("error", "error", "NULL when no error or string describing error",
162 NULL, G_PARAM_READABLE));
163 g_object_class_install_property (object_class, PROP_OPEN,
164 g_param_spec_boolean ("open", "open", "TRUE while data is flowing",
165 FALSE, G_PARAM_READABLE));
166 g_object_class_install_property (object_class, PROP_COMPLETE,
167 g_param_spec_boolean ("complete", "complete", "TRUE when all data has been transmitted",
168 FALSE, G_PARAM_READABLE));
171 static void
172 swfdec_stream_init (SwfdecStream *stream)
174 SwfdecStreamPrivate *priv;
176 stream->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (stream, SWFDEC_TYPE_STREAM, SwfdecStreamPrivate);
178 priv->queue = swfdec_buffer_queue_new ();
181 /*** INTERNAL API ***/
183 SwfdecBufferQueue *
184 swfdec_stream_get_queue (SwfdecStream *stream)
186 g_return_val_if_fail (SWFDEC_IS_STREAM (stream), NULL);
188 return stream->priv->queue;
191 static void swfdec_stream_queue_processing (SwfdecStream *stream);
193 static void
194 swfdec_stream_process (gpointer streamp, gpointer unused)
196 SwfdecStream *stream = streamp;
197 SwfdecStreamPrivate *priv = stream->priv;
199 g_assert (priv->target != NULL);
201 priv->queued = FALSE;
202 if (priv->state == priv->processed_state &&
203 priv->state != SWFDEC_STREAM_STATE_OPEN)
204 return;
205 g_assert (priv->processed_state != SWFDEC_STREAM_STATE_CLOSED);
206 g_object_ref (stream);
207 if (priv->state == SWFDEC_STREAM_STATE_ERROR) {
208 swfdec_stream_target_error (priv->target, stream);
209 } else {
210 while (priv->state != priv->processed_state) {
211 if (priv->processed_state == SWFDEC_STREAM_STATE_CONNECTING) {
212 priv->processed_state = SWFDEC_STREAM_STATE_OPEN;
213 swfdec_stream_target_open (priv->target, stream);
214 } else if (priv->processed_state == SWFDEC_STREAM_STATE_OPEN) {
215 if (swfdec_stream_target_parse (priv->target, stream)) {
216 swfdec_stream_queue_processing (stream);
217 goto out;
218 } else if (priv->target) {
219 priv->processed_state = SWFDEC_STREAM_STATE_CLOSED;
220 swfdec_stream_target_close (priv->target, stream);
221 } else {
222 goto out;
226 if (priv->processed_state == SWFDEC_STREAM_STATE_OPEN) {
227 if (swfdec_stream_target_parse (priv->target, stream)) {
228 swfdec_stream_queue_processing (stream);
232 out:
233 g_object_unref (stream);
236 static void
237 swfdec_stream_queue_processing (SwfdecStream *stream)
239 SwfdecStreamPrivate *priv = stream->priv;
241 if (priv->queued)
242 return;
243 priv->queued = TRUE;
244 if (priv->target) {
245 g_assert (priv->player);
246 swfdec_player_add_external_action (priv->player, stream,
247 swfdec_stream_process, NULL);
251 void
252 swfdec_stream_ensure_closed (SwfdecStream *stream)
254 SwfdecStreamPrivate *priv;
255 SwfdecStreamClass *klass;
257 g_return_if_fail (SWFDEC_IS_STREAM (stream));
259 priv = stream->priv;
260 if (priv->state == SWFDEC_STREAM_STATE_ERROR ||
261 priv->state == SWFDEC_STREAM_STATE_CLOSED)
262 return;
264 klass = SWFDEC_STREAM_GET_CLASS (stream);
266 if (klass->close)
267 klass->close (stream);
268 priv->state = SWFDEC_STREAM_STATE_CLOSED;
269 priv->processed_state = SWFDEC_STREAM_STATE_CLOSED;
272 void
273 swfdec_stream_set_target (SwfdecStream *stream, SwfdecStreamTarget *target)
275 SwfdecStreamPrivate *priv;
277 g_return_if_fail (SWFDEC_IS_STREAM (stream));
278 if (target != NULL) {
279 g_return_if_fail (stream->priv->processed_state == SWFDEC_STREAM_STATE_CONNECTING);
280 g_return_if_fail (SWFDEC_IS_STREAM_TARGET (target));
283 priv = stream->priv;
284 if (priv->target) {
285 swfdec_player_remove_all_external_actions (priv->player, stream);
287 priv->queued = FALSE;
288 priv->target = target;
289 if (target) {
290 priv->player = swfdec_stream_target_get_player (target);
291 if (priv->state != SWFDEC_STREAM_STATE_CONNECTING)
292 swfdec_stream_queue_processing (stream);
293 } else {
294 priv->player = NULL;
298 /** PUBLIC API ***/
301 * swfdec_stream_describe:
302 * @stream: a #SwfdecStream
304 * Describes the stream in a simple string. This is mostly useful for debugging
305 * purposes.
307 * Returns: a constant string describing the stream
309 const char *
310 swfdec_stream_describe (SwfdecStream *stream)
312 SwfdecStreamClass *klass;
314 g_return_val_if_fail (SWFDEC_IS_STREAM (stream), NULL);
316 klass = SWFDEC_STREAM_GET_CLASS (stream);
317 g_return_val_if_fail (klass->describe, NULL);
319 return klass->describe (stream);
323 * swfdec_stream_error:
324 * @stream: a #SwfdecStream
325 * @error: a printf-style string describing the error
326 * @...: arguments for the @error string
328 * Moves the stream in the error state if it wasn't before. A stream that is in
329 * the error state will not process any more data. Also, internal error
330 * handling scripts may be executed.
332 void
333 swfdec_stream_error (SwfdecStream *stream, const char *error, ...)
335 va_list args;
337 g_return_if_fail (SWFDEC_IS_STREAM (stream));
338 g_return_if_fail (error != NULL);
340 va_start (args, error);
341 swfdec_stream_errorv (stream, error, args);
342 va_end (args);
346 * swfdec_stream_errorv:
347 * @stream: a #SwfdecStream
348 * @error: a printf-style error string
349 * @args: arguments for @error
351 * This function is the va_list alternative to swfdec_stream_error(). See that
352 * function for details.
354 void
355 swfdec_stream_errorv (SwfdecStream *stream, const char *error, va_list args)
357 SwfdecStreamPrivate *priv;
358 char *real_error;
360 g_return_if_fail (SWFDEC_IS_STREAM (stream));
361 g_return_if_fail (error != NULL);
363 real_error = g_strdup_vprintf (error, args);
364 priv = stream->priv;
365 if (priv->error) {
366 SWFDEC_ERROR ("another error in stream for %s: %s",
367 swfdec_stream_describe (stream), real_error);
368 g_free (real_error);
369 return;
372 SWFDEC_ERROR ("error in stream for %s: %s",
373 swfdec_stream_describe (stream), real_error);
374 priv->state = SWFDEC_STREAM_STATE_ERROR;
375 priv->error = real_error;
376 swfdec_stream_queue_processing (stream);
380 * swfdec_stream_open:
381 * @stream: a #SwfdecStream
383 * Call this function when your stream opened the resulting file. For HTTP this
384 * is when having received the headers. You must call this function before
385 * swfdec_stream_push() can be called.
387 void
388 swfdec_stream_open (SwfdecStream *stream)
390 g_return_if_fail (SWFDEC_IS_STREAM (stream));
391 g_return_if_fail (stream->priv->state == SWFDEC_STREAM_STATE_CONNECTING);
393 stream->priv->state = SWFDEC_STREAM_STATE_OPEN;
394 g_object_notify (G_OBJECT (stream), "open");
395 swfdec_stream_queue_processing (stream);
399 * swfdec_stream_is_open:
400 * @stream: a #SwfdecStream
402 * Checks if the given @stream is currrently open. Some functions, for example
403 * swfdec_socket_send(), require an open stream.
405 * Returns: %TRUE if the stream is open, %FALSE otherwise.
407 gboolean
408 swfdec_stream_is_open (SwfdecStream *stream)
410 g_return_val_if_fail (SWFDEC_IS_STREAM (stream), FALSE);
412 return stream->priv->state == SWFDEC_STREAM_STATE_OPEN;
416 * swfdec_stream_is_complete:
417 * @stream: a #SwfdecStream
419 * Checks if all data has successfully been transmitted through the @stream
420 * and it has been closed.
422 * Returns: %TRUE if the stream is completed, %FALSE otherwise.
424 gboolean
425 swfdec_stream_is_complete (SwfdecStream *stream)
427 g_return_val_if_fail (SWFDEC_IS_STREAM (stream), FALSE);
429 return stream->priv->state == SWFDEC_STREAM_STATE_CLOSED;
433 * swfdec_stream_push:
434 * @stream: a #SwfdecStream
435 * @buffer: new data to make available. The stream takes the reference
436 * to the buffer.
438 * Makes the data in @buffer available to @stream and processes it. The @stream
439 * must be open.
441 void
442 swfdec_stream_push (SwfdecStream *stream, SwfdecBuffer *buffer)
444 g_return_if_fail (SWFDEC_IS_STREAM (stream));
445 g_return_if_fail (stream->priv->state == SWFDEC_STREAM_STATE_OPEN);
446 g_return_if_fail (buffer != NULL);
448 swfdec_buffer_queue_push (stream->priv->queue, buffer);
449 /* FIXME */
450 if (SWFDEC_IS_LOADER (stream))
451 g_object_notify (G_OBJECT (stream), "loaded");
452 swfdec_stream_queue_processing (stream);
456 * swfdec_stream_close:
457 * @stream: a #SwfdecStream
459 * Indicates to @stream that no more data will follow. The stream must be open.
461 void
462 swfdec_stream_close (SwfdecStream *stream)
464 g_return_if_fail (SWFDEC_IS_STREAM (stream));
465 g_return_if_fail (stream->priv->state == SWFDEC_STREAM_STATE_OPEN);
467 stream->priv->state = SWFDEC_STREAM_STATE_CLOSED;
468 g_object_notify (G_OBJECT (stream), "open");
469 g_object_notify (G_OBJECT (stream), "complete");
470 swfdec_stream_queue_processing (stream);
473 /* FIXME: put in right file */
474 static void
475 swfdec_socket_process_writable (gpointer streamp, gpointer unused)
477 SwfdecStream *stream = streamp;
478 SwfdecStreamPrivate *priv = stream->priv;
480 g_assert (priv->target);
482 swfdec_stream_target_writable (priv->target, stream);
486 * swfdec_socket_signal_writable:
487 * @sock: the socket that has become writable
489 * Signals to Swfdec that it should try writing to the given socket again.
491 void
492 swfdec_socket_signal_writable (SwfdecSocket *sock)
494 SwfdecStreamPrivate *priv;
496 g_return_if_fail (SWFDEC_IS_SOCKET (sock));
498 priv = SWFDEC_STREAM (sock)->priv;
499 if (priv->target) {
500 g_assert (priv->player);
501 swfdec_player_add_external_action (priv->player, sock,
502 swfdec_socket_process_writable, NULL);