fix jsut committed crasher by rewriting startDrag action
[swfdec.git] / libswfdec / swfdec_net_stream.c
blobfac08862a0c88a8490da34f92fbab9fb7e61a41b
1 /* Swfdec
2 * Copyright (C) 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.
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 <math.h>
25 #include "swfdec_net_stream.h"
26 #include "swfdec_amf.h"
27 #include "swfdec_as_strings.h"
28 #include "swfdec_audio_flv.h"
29 #include "swfdec_debug.h"
30 #include "swfdec_loader_internal.h"
31 #include "swfdec_loadertarget.h"
33 /* NB: code and level must be rooted gc-strings */
34 static void
35 swfdec_net_stream_onstatus (SwfdecNetStream *stream, const char *code, const char *level)
37 SwfdecAsValue val;
38 SwfdecAsObject *object;
40 object = swfdec_as_object_new (SWFDEC_AS_OBJECT (stream)->context);
41 if (!object)
42 return;
43 SWFDEC_INFO ("emitting onStatus for %s %s", level, code);
44 SWFDEC_AS_VALUE_SET_STRING (&val, code);
45 swfdec_as_object_set_variable (object, SWFDEC_AS_STR_code, &val);
46 SWFDEC_AS_VALUE_SET_STRING (&val, level);
47 swfdec_as_object_set_variable (object, SWFDEC_AS_STR_level, &val);
49 SWFDEC_AS_VALUE_SET_OBJECT (&val, object);
50 swfdec_as_object_call (SWFDEC_AS_OBJECT (stream), SWFDEC_AS_STR_onStatus, 1, &val, NULL);
53 static void swfdec_net_stream_update_playing (SwfdecNetStream *stream);
54 static void
55 swfdec_net_stream_video_goto (SwfdecNetStream *stream, guint timestamp)
57 SwfdecBuffer *buffer;
58 SwfdecVideoFormat format;
59 cairo_surface_t *old;
60 gboolean process_events;
61 guint process_events_from;
63 SWFDEC_LOG ("goto %ums", timestamp);
64 process_events = timestamp == stream->next_time;
65 process_events_from = MIN (stream->next_time, stream->current_time + 1);
66 old = stream->surface;
67 if (stream->surface) {
68 cairo_surface_destroy (stream->surface);
69 stream->surface = NULL;
71 if (stream->flvdecoder->video) {
72 buffer = swfdec_flv_decoder_get_video (stream->flvdecoder, timestamp,
73 FALSE, &format, &stream->current_time, &stream->next_time);
74 } else {
75 buffer = NULL;
77 if (buffer == NULL) {
78 SWFDEC_ERROR ("got no buffer - no video available?");
79 } else {
80 if (format != stream->format) {
81 if (stream->decoder)
82 swfdec_video_decoder_free (stream->decoder);
83 stream->format = format;
84 stream->decoder = swfdec_video_decoder_new (format);
86 if (stream->decoder) {
87 stream->surface = swfdec_video_decoder_decode (stream->decoder, buffer);
89 if (stream->surface) {
90 GList *walk;
91 for (walk = stream->movies; walk; walk = walk->next) {
92 swfdec_video_movie_new_image (walk->data, stream->surface);
96 if (stream->next_time <= stream->current_time) {
97 if (swfdec_flv_decoder_is_eof (stream->flvdecoder)) {
98 swfdec_net_stream_onstatus (stream, SWFDEC_AS_STR_NetStream_Play_Stop, SWFDEC_AS_STR_status);
99 } else {
100 stream->buffering = TRUE;
101 swfdec_net_stream_onstatus (stream, SWFDEC_AS_STR_NetStream_Buffer_Empty,
102 SWFDEC_AS_STR_status);
104 swfdec_net_stream_update_playing (stream);
106 if (process_events) {
107 while (process_events_from <= stream->current_time) {
108 SwfdecAsValue name, value;
109 SwfdecBits bits;
110 SwfdecBuffer *event = swfdec_flv_decoder_get_data (stream->flvdecoder, process_events_from, &process_events_from);
111 if (!event)
112 break;
113 SWFDEC_LOG ("processing event from timestamp %u", process_events_from);
114 process_events_from++; /* increase so we get the next event next time */
115 swfdec_bits_init (&bits, event);
116 if (swfdec_amf_parse (SWFDEC_AS_OBJECT (stream)->context, &bits, 2,
117 SWFDEC_AMF_STRING, &name, SWFDEC_AMF_MIXED_ARRAY, &value) != 2) {
118 SWFDEC_ERROR ("could not parse data tag");
119 } else {
120 swfdec_as_object_call (SWFDEC_AS_OBJECT (stream),
121 SWFDEC_AS_VALUE_GET_STRING (&name), 1, &value, NULL);
127 static void
128 swfdec_net_stream_timeout (SwfdecTimeout *timeout)
130 SwfdecNetStream *stream = SWFDEC_NET_STREAM ((guchar *) timeout - G_STRUCT_OFFSET (SwfdecNetStream, timeout));
132 SWFDEC_LOG ("timeout fired");
133 stream->timeout.callback = NULL;
134 swfdec_net_stream_video_goto (stream, stream->next_time);
135 if (stream->next_time > stream->current_time) {
136 SWFDEC_LOG ("readding timeout");
137 stream->timeout.timestamp += SWFDEC_MSECS_TO_TICKS (stream->next_time - stream->current_time);
138 stream->timeout.callback = swfdec_net_stream_timeout;
139 swfdec_player_add_timeout (SWFDEC_PLAYER (SWFDEC_AS_OBJECT (stream)->context), &stream->timeout);
140 } else {
141 if (stream->audio) {
142 /* FIXME: just unref and let it take care of removing itself? */
143 SWFDEC_LOG ("stopping audio due to EOS");
144 swfdec_audio_remove (stream->audio);
145 g_object_unref (stream->audio);
146 stream->audio = NULL;
151 static void
152 swfdec_net_stream_update_playing (SwfdecNetStream *stream)
154 SwfdecPlayer *player = SWFDEC_PLAYER (SWFDEC_AS_OBJECT (stream)->context);
155 gboolean should_play;
157 should_play = stream->playing; /* checks user-set play/pause */
158 should_play &= !stream->buffering; /* checks enough data is available */
159 should_play &= stream->flvdecoder != NULL; /* checks there even is something to play */
160 should_play &= stream->next_time > stream->current_time; /* checks if EOF */
161 if (should_play && stream->timeout.callback == NULL) {
162 SWFDEC_DEBUG ("starting playback");
163 stream->timeout.callback = swfdec_net_stream_timeout;
164 stream->timeout.timestamp = player->time + SWFDEC_MSECS_TO_TICKS (stream->next_time - stream->current_time);
165 swfdec_player_add_timeout (player, &stream->timeout);
166 if (stream->flvdecoder->audio) {
167 g_assert (stream->audio == NULL);
168 SWFDEC_LOG ("starting audio");
169 stream->audio = swfdec_audio_flv_new (player,
170 stream->flvdecoder, stream->current_time);
171 } else {
172 SWFDEC_LOG ("no audio");
174 } else if (!should_play && stream->timeout.callback != NULL) {
175 if (stream->audio) {
176 SWFDEC_LOG ("stopping audio");
177 swfdec_audio_remove (stream->audio);
178 g_object_unref (stream->audio);
179 stream->audio = NULL;
181 swfdec_player_remove_timeout (player, &stream->timeout);
182 stream->timeout.callback = NULL;
183 SWFDEC_DEBUG ("stopping playback");
187 /*** SWFDEC_LOADER_TARGET interface ***/
189 static SwfdecPlayer *
190 swfdec_net_stream_loader_target_get_player (SwfdecLoaderTarget *target)
192 return SWFDEC_PLAYER (SWFDEC_AS_OBJECT (target)->context);
195 static void
196 swfdec_net_stream_loader_target_error (SwfdecLoaderTarget *target,
197 SwfdecLoader *loader)
199 SwfdecNetStream *stream = SWFDEC_NET_STREAM (target);
201 if (stream->flvdecoder == NULL)
202 swfdec_net_stream_onstatus (stream, SWFDEC_AS_STR_NetStream_Play_StreamNotFound,
203 SWFDEC_AS_STR_error);
206 static void
207 swfdec_net_stream_loader_target_recheck (SwfdecNetStream *stream)
209 if (stream->buffering) {
210 guint first, last;
211 if (swfdec_flv_decoder_get_video_info (stream->flvdecoder, &first, &last)) {
212 guint current = MAX (first, stream->current_time);
213 if (current + stream->buffer_time <= last) {
214 swfdec_net_stream_video_goto (stream, current);
215 stream->buffering = FALSE;
216 swfdec_net_stream_onstatus (stream, SWFDEC_AS_STR_NetStream_Buffer_Full,
217 SWFDEC_AS_STR_status);
219 } else {
220 SWFDEC_ERROR ("no video stream, how do we update buffering?");
223 swfdec_net_stream_update_playing (stream);
226 static void
227 swfdec_net_stream_loader_target_parse (SwfdecLoaderTarget *target,
228 SwfdecLoader *loader)
230 SwfdecNetStream *stream = SWFDEC_NET_STREAM (target);
231 SwfdecDecoderClass *klass;
232 gboolean recheck = FALSE;
234 if (loader->state != SWFDEC_LOADER_STATE_EOF && swfdec_buffer_queue_get_depth (loader->queue) == 0) {
235 SWFDEC_INFO ("nothing to do");
236 return;
238 if (stream->flvdecoder == NULL) {
239 /* FIXME: add mp3 support */
240 stream->flvdecoder = g_object_new (SWFDEC_TYPE_FLV_DECODER, NULL);
241 SWFDEC_DECODER (stream->flvdecoder)->player = SWFDEC_PLAYER (SWFDEC_AS_OBJECT (stream)->context);
242 SWFDEC_DECODER (stream->flvdecoder)->queue = loader->queue;
243 swfdec_net_stream_onstatus (stream, SWFDEC_AS_STR_NetStream_Play_Start,
244 SWFDEC_AS_STR_status);
245 swfdec_loader_set_data_type (loader, SWFDEC_LOADER_DATA_FLV);
247 klass = SWFDEC_DECODER_GET_CLASS (stream->flvdecoder);
248 g_return_if_fail (klass->parse);
250 while (TRUE) {
251 SwfdecStatus status = klass->parse (SWFDEC_DECODER (stream->flvdecoder));
252 switch (status) {
253 case SWFDEC_STATUS_OK:
254 break;
255 case SWFDEC_STATUS_INIT:
256 /* HACK for native flv playback */
257 swfdec_player_initialize (SWFDEC_PLAYER (SWFDEC_AS_OBJECT (stream)->context), 7,
258 SWFDEC_DECODER (stream->flvdecoder)->rate,
259 SWFDEC_DECODER (stream->flvdecoder)->width,
260 SWFDEC_DECODER (stream->flvdecoder)->height);
261 case SWFDEC_STATUS_IMAGE:
262 recheck = TRUE;
263 break;
264 case SWFDEC_STATUS_ERROR:
265 case SWFDEC_STATUS_NEEDBITS:
266 case SWFDEC_STATUS_EOF:
267 goto out;
268 default:
269 g_assert_not_reached ();
270 return;
273 out:
274 if (recheck)
275 swfdec_net_stream_loader_target_recheck (stream);
278 static void
279 swfdec_net_stream_loader_target_eof (SwfdecLoaderTarget *target,
280 SwfdecLoader *loader)
282 SwfdecNetStream *stream = SWFDEC_NET_STREAM (target);
283 guint first, last;
285 swfdec_flv_decoder_eof (stream->flvdecoder);
286 swfdec_net_stream_onstatus (stream, SWFDEC_AS_STR_NetStream_Buffer_Flush,
287 SWFDEC_AS_STR_status);
288 swfdec_net_stream_video_goto (stream, stream->current_time);
289 stream->buffering = FALSE;
290 if (swfdec_flv_decoder_get_video_info (stream->flvdecoder, &first, &last) &&
291 stream->current_time + stream->buffer_time <= last) {
292 swfdec_net_stream_onstatus (stream, SWFDEC_AS_STR_NetStream_Buffer_Full,
293 SWFDEC_AS_STR_status);
295 swfdec_net_stream_loader_target_recheck (stream);
298 static void
299 swfdec_net_stream_loader_target_init (SwfdecLoaderTargetInterface *iface)
301 iface->get_player = swfdec_net_stream_loader_target_get_player;
302 iface->parse = swfdec_net_stream_loader_target_parse;
303 iface->eof = swfdec_net_stream_loader_target_eof;
304 iface->error = swfdec_net_stream_loader_target_error;
307 /*** SWFDEC VIDEO MOVIE INPUT ***/
309 static void
310 swfdec_net_stream_input_connect (SwfdecVideoMovieInput *input, SwfdecVideoMovie *movie)
312 SwfdecNetStream *stream = SWFDEC_NET_STREAM ((guchar *) input - G_STRUCT_OFFSET (SwfdecNetStream, input));
314 stream->movies = g_list_prepend (stream->movies, movie);
315 g_object_ref (stream);
318 static void
319 swfdec_net_stream_input_disconnect (SwfdecVideoMovieInput *input, SwfdecVideoMovie *movie)
321 SwfdecNetStream *stream = SWFDEC_NET_STREAM ((guchar *) input - G_STRUCT_OFFSET (SwfdecNetStream, input));
323 stream->movies = g_list_remove (stream->movies, movie);
324 g_object_unref (stream);
327 /*** SWFDEC_NET_STREAM ***/
329 G_DEFINE_TYPE_WITH_CODE (SwfdecNetStream, swfdec_net_stream, SWFDEC_TYPE_AS_OBJECT,
330 G_IMPLEMENT_INTERFACE (SWFDEC_TYPE_LOADER_TARGET, swfdec_net_stream_loader_target_init))
332 static void
333 swfdec_net_stream_dispose (GObject *object)
335 SwfdecNetStream *stream = SWFDEC_NET_STREAM (object);
337 swfdec_net_stream_set_playing (stream, FALSE);
338 if (stream->surface) {
339 cairo_surface_destroy (stream->surface);
340 stream->surface = NULL;
342 if (stream->decoder) {
343 swfdec_video_decoder_free (stream->decoder);
344 stream->decoder = NULL;
346 swfdec_net_stream_set_loader (stream, NULL);
347 g_assert (stream->movies == NULL);
349 G_OBJECT_CLASS (swfdec_net_stream_parent_class)->dispose (object);
352 static gboolean
353 swfdec_net_stream_get_variable (SwfdecAsObject *object, SwfdecAsObject *orig,
354 const char *variable, SwfdecAsValue *val, guint *flags)
356 SwfdecNetStream *stream;
358 if (SWFDEC_AS_OBJECT_CLASS (swfdec_net_stream_parent_class)->get (object, orig, variable, val, flags))
359 return TRUE;
361 stream = SWFDEC_NET_STREAM (object);
362 /* FIXME: need case insensitive comparisons? */
363 if (variable == SWFDEC_AS_STR_time) {
364 guint msecs;
365 if (stream->flvdecoder == NULL ||
366 !swfdec_flv_decoder_get_video_info (stream->flvdecoder, &msecs, NULL)) {
367 SWFDEC_AS_VALUE_SET_INT (val, 0);
368 } else {
369 if (msecs >= stream->current_time)
370 msecs = 0;
371 else
372 msecs = stream->current_time - msecs;
374 SWFDEC_AS_VALUE_SET_NUMBER (val, msecs / 1000.);
375 *flags = 0;
376 return TRUE;
377 } else if (variable == SWFDEC_AS_STR_bytesLoaded) {
378 if (stream->loader == NULL)
379 SWFDEC_AS_VALUE_SET_INT (val, 0);
380 else
381 SWFDEC_AS_VALUE_SET_INT (val, swfdec_loader_get_loaded (stream->loader));
382 *flags = 0;
383 return TRUE;
384 } else if (variable == SWFDEC_AS_STR_bytesTotal) {
385 guint bytes;
386 if (stream->loader == NULL) {
387 bytes = 0;
388 } else {
389 bytes = swfdec_loader_get_size (stream->loader);
390 if (bytes == 0)
391 bytes = swfdec_loader_get_loaded (stream->loader);
393 SWFDEC_AS_VALUE_SET_INT (val, bytes);
394 *flags = 0;
395 return TRUE;
397 return FALSE;
400 static void
401 swfdec_net_stream_mark (SwfdecAsObject *object)
403 SwfdecNetStream *stream = SWFDEC_NET_STREAM (object);
405 swfdec_as_object_mark (SWFDEC_AS_OBJECT (stream->conn));
407 SWFDEC_AS_OBJECT_CLASS (swfdec_net_stream_parent_class)->mark (object);
410 static void
411 swfdec_net_stream_class_init (SwfdecNetStreamClass *klass)
413 GObjectClass *object_class = G_OBJECT_CLASS (klass);
414 SwfdecAsObjectClass *asobject_class = SWFDEC_AS_OBJECT_CLASS (klass);
416 object_class->dispose = swfdec_net_stream_dispose;
418 asobject_class->get = swfdec_net_stream_get_variable;
419 asobject_class->mark = swfdec_net_stream_mark;
422 static void
423 swfdec_net_stream_init (SwfdecNetStream *stream)
425 stream->input.connect = swfdec_net_stream_input_connect;
426 stream->input.disconnect = swfdec_net_stream_input_disconnect;
428 stream->buffer_time = 100; /* msecs */
431 SwfdecNetStream *
432 swfdec_net_stream_new (SwfdecNetConnection *conn)
434 SwfdecAsContext *context;
435 SwfdecNetStream *stream;
437 g_return_val_if_fail (SWFDEC_IS_NET_CONNECTION (conn), NULL);
439 context = SWFDEC_AS_OBJECT (conn)->context;
440 if (!swfdec_as_context_use_mem (context, sizeof (SwfdecNetStream)))
441 return NULL;
442 stream = g_object_new (SWFDEC_TYPE_NET_STREAM, NULL);
443 swfdec_as_object_add (SWFDEC_AS_OBJECT (stream), context, sizeof (SwfdecNetStream));
444 stream->conn = conn;
446 return stream;
449 void
450 swfdec_net_stream_set_url (SwfdecNetStream *stream, const char *url)
452 SwfdecLoader *loader;
454 g_return_if_fail (SWFDEC_IS_NET_STREAM (stream));
455 g_return_if_fail (url != NULL);
457 /* FIXME: use the connection once connections are implemented */
458 loader = swfdec_player_load (SWFDEC_PLAYER (SWFDEC_AS_OBJECT (stream)->context), url);
459 swfdec_net_stream_set_loader (stream, loader);
460 g_object_unref (loader);
463 void
464 swfdec_net_stream_set_loader (SwfdecNetStream *stream, SwfdecLoader *loader)
466 g_return_if_fail (SWFDEC_IS_NET_STREAM (stream));
467 g_return_if_fail (loader == NULL || SWFDEC_IS_LOADER (loader));
469 if (stream->loader) {
470 swfdec_loader_set_target (stream->loader, NULL);
471 swfdec_loader_close (stream->loader);
472 g_object_unref (stream->loader);
474 if (stream->flvdecoder) {
475 g_object_unref (stream->flvdecoder);
476 stream->flvdecoder = NULL;
478 stream->loader = loader;
479 stream->buffering = TRUE;
480 if (loader) {
481 g_object_ref (loader);
482 swfdec_loader_set_target (loader, SWFDEC_LOADER_TARGET (stream));
484 swfdec_net_stream_set_playing (stream, TRUE);
487 void
488 swfdec_net_stream_set_playing (SwfdecNetStream *stream, gboolean playing)
490 g_return_if_fail (SWFDEC_IS_NET_STREAM (stream));
492 stream->playing = playing;
494 swfdec_net_stream_update_playing (stream);
497 gboolean
498 swfdec_net_stream_get_playing (SwfdecNetStream *stream)
500 g_return_val_if_fail (SWFDEC_IS_NET_STREAM (stream), FALSE);
502 return stream->playing;
505 void
506 swfdec_net_stream_set_buffer_time (SwfdecNetStream *stream, double secs)
508 g_return_if_fail (SWFDEC_IS_NET_STREAM (stream));
510 /* FIXME: is this correct? */
511 if (secs <= 0)
512 return;
514 stream->buffer_time = secs * 1000;
517 double
518 swfdec_net_stream_get_buffer_time (SwfdecNetStream *stream)
520 g_return_val_if_fail (SWFDEC_IS_NET_STREAM (stream), 0.1);
522 return (double) stream->buffer_time / 1000.0;
525 void
526 swfdec_net_stream_seek (SwfdecNetStream *stream, double secs)
528 guint first, last, msecs;
530 g_return_if_fail (SWFDEC_IS_NET_STREAM (stream));
532 if (stream->flvdecoder == NULL)
533 return;
534 if (!isfinite (secs) || secs < 0) {
535 SWFDEC_ERROR ("seeking to %g doesn't work", secs);
536 return;
538 if (!swfdec_flv_decoder_get_video_info (stream->flvdecoder, &first, &last)) {
539 SWFDEC_ERROR ("FIXME: implement seeking in audio only NetStream");
540 return;
542 msecs = secs * 1000;
543 msecs += first;
544 if (msecs > last)
545 msecs = last;
546 swfdec_flv_decoder_get_video (stream->flvdecoder, msecs, TRUE, NULL, &msecs, NULL);
547 swfdec_net_stream_video_goto (stream, msecs);
548 /* FIXME: this needs to be implemented correctly, but requires changes to audio handling:
549 * - creating a new audio stream will cause attachAudio scripts to lose information
550 * - implementing seek on audio stream requires a SwfdecAudio::changed signal so audio
551 * backends can react correctly.
553 if (stream->audio) {
554 SWFDEC_WARNING ("FIXME: restarting audio after seek");
555 swfdec_audio_remove (stream->audio);
556 g_object_unref (stream->audio);
557 stream->audio = swfdec_audio_flv_new (SWFDEC_PLAYER (SWFDEC_AS_OBJECT (stream)->context),
558 stream->flvdecoder, stream->current_time);