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.
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_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 */
35 swfdec_net_stream_onstatus (SwfdecNetStream
*stream
, const char *code
, const char *level
)
38 SwfdecAsObject
*object
;
40 object
= swfdec_as_object_new (SWFDEC_AS_OBJECT (stream
)->context
);
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
);
55 swfdec_net_stream_video_goto (SwfdecNetStream
*stream
, guint timestamp
)
58 SwfdecVideoFormat format
;
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
);
78 SWFDEC_ERROR ("got no buffer - no video available?");
80 if (format
!= stream
->format
) {
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
) {
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
);
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
;
110 SwfdecBuffer
*event
= swfdec_flv_decoder_get_data (stream
->flvdecoder
, process_events_from
, &process_events_from
);
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");
120 swfdec_as_object_call (SWFDEC_AS_OBJECT (stream
),
121 SWFDEC_AS_VALUE_GET_STRING (&name
), 1, &value
, NULL
);
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
);
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
;
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
);
172 SWFDEC_LOG ("no audio");
174 } else if (!should_play
&& stream
->timeout
.callback
!= NULL
) {
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
);
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
);
207 swfdec_net_stream_loader_target_recheck (SwfdecNetStream
*stream
)
209 if (stream
->buffering
) {
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
);
220 SWFDEC_ERROR ("no video stream, how do we update buffering?");
223 swfdec_net_stream_update_playing (stream
);
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");
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
);
251 SwfdecStatus status
= klass
->parse (SWFDEC_DECODER (stream
->flvdecoder
));
253 case SWFDEC_STATUS_OK
:
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
:
264 case SWFDEC_STATUS_ERROR
:
265 case SWFDEC_STATUS_NEEDBITS
:
266 case SWFDEC_STATUS_EOF
:
269 g_assert_not_reached ();
275 swfdec_net_stream_loader_target_recheck (stream
);
279 swfdec_net_stream_loader_target_eof (SwfdecLoaderTarget
*target
,
280 SwfdecLoader
*loader
)
282 SwfdecNetStream
*stream
= SWFDEC_NET_STREAM (target
);
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
);
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 ***/
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
);
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
))
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
);
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
))
361 stream
= SWFDEC_NET_STREAM (object
);
362 /* FIXME: need case insensitive comparisons? */
363 if (variable
== SWFDEC_AS_STR_time
) {
365 if (stream
->flvdecoder
== NULL
||
366 !swfdec_flv_decoder_get_video_info (stream
->flvdecoder
, &msecs
, NULL
)) {
367 SWFDEC_AS_VALUE_SET_INT (val
, 0);
369 if (msecs
>= stream
->current_time
)
372 msecs
= stream
->current_time
- msecs
;
374 SWFDEC_AS_VALUE_SET_NUMBER (val
, msecs
/ 1000.);
377 } else if (variable
== SWFDEC_AS_STR_bytesLoaded
) {
378 if (stream
->loader
== NULL
)
379 SWFDEC_AS_VALUE_SET_INT (val
, 0);
381 SWFDEC_AS_VALUE_SET_INT (val
, swfdec_loader_get_loaded (stream
->loader
));
384 } else if (variable
== SWFDEC_AS_STR_bytesTotal
) {
386 if (stream
->loader
== NULL
) {
389 bytes
= swfdec_loader_get_size (stream
->loader
);
391 bytes
= swfdec_loader_get_loaded (stream
->loader
);
393 SWFDEC_AS_VALUE_SET_INT (val
, bytes
);
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
);
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
;
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 */
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
)))
442 stream
= g_object_new (SWFDEC_TYPE_NET_STREAM
, NULL
);
443 swfdec_as_object_add (SWFDEC_AS_OBJECT (stream
), context
, sizeof (SwfdecNetStream
));
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
);
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
;
481 g_object_ref (loader
);
482 swfdec_loader_set_target (loader
, SWFDEC_LOADER_TARGET (stream
));
484 swfdec_net_stream_set_playing (stream
, TRUE
);
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
);
498 swfdec_net_stream_get_playing (SwfdecNetStream
*stream
)
500 g_return_val_if_fail (SWFDEC_IS_NET_STREAM (stream
), FALSE
);
502 return stream
->playing
;
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? */
514 stream
->buffer_time
= secs
* 1000;
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;
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
)
534 if (!isfinite (secs
) || secs
< 0) {
535 SWFDEC_ERROR ("seeking to %g doesn't work", secs
);
538 if (!swfdec_flv_decoder_get_video_info (stream
->flvdecoder
, &first
, &last
)) {
539 SWFDEC_ERROR ("FIXME: implement seeking in audio only NetStream");
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.
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
);