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 cairo_surface_t
*
54 swfdec_net_stream_decode_video (SwfdecNetStream
*stream
, SwfdecBuffer
*buffer
)
56 SwfdecVideoDecoder
*decoder
= stream
->decoder
;
57 cairo_surface_t
*surface
;
62 if (decoder
->codec
== SWFDEC_VIDEO_CODEC_VP6
||
63 decoder
->codec
== SWFDEC_VIDEO_CODEC_VP6_ALPHA
) {
69 tmp
= swfdec_buffer_new_subbuffer (buffer
, 1, buffer
->length
- 1);
70 surface
= swfdec_video_decoder_decode (decoder
, tmp
);
71 swfdec_buffer_unref (tmp
);
73 SWFDEC_FIXME ("need to subtract %ux%u pixels", wsub
, hsub
);
76 surface
= swfdec_video_decoder_decode (decoder
, buffer
);
81 static void swfdec_net_stream_update_playing (SwfdecNetStream
*stream
);
83 swfdec_net_stream_video_goto (SwfdecNetStream
*stream
, guint timestamp
)
86 SwfdecVideoCodec format
;
88 gboolean process_events
;
89 guint process_events_from
;
91 SWFDEC_LOG ("goto %ums", timestamp
);
92 process_events
= timestamp
== stream
->next_time
;
93 process_events_from
= MIN (stream
->next_time
, stream
->current_time
+ 1);
94 old
= stream
->surface
;
95 if (stream
->surface
) {
96 cairo_surface_destroy (stream
->surface
);
97 stream
->surface
= NULL
;
99 if (stream
->flvdecoder
->video
) {
100 buffer
= swfdec_flv_decoder_get_video (stream
->flvdecoder
, timestamp
,
101 FALSE
, &format
, &stream
->current_time
, &stream
->next_time
);
105 if (buffer
== NULL
) {
106 SWFDEC_ERROR ("got no buffer - no video available?");
108 if (format
!= stream
->format
) {
110 swfdec_video_decoder_free (stream
->decoder
);
111 stream
->format
= format
;
112 stream
->decoder
= swfdec_video_decoder_new (format
);
114 stream
->surface
= swfdec_net_stream_decode_video (stream
, buffer
);
115 if (stream
->surface
) {
117 for (walk
= stream
->movies
; walk
; walk
= walk
->next
) {
118 swfdec_video_movie_new_image (walk
->data
, stream
->surface
);
122 if (stream
->next_time
<= stream
->current_time
) {
123 if (swfdec_flv_decoder_is_eof (stream
->flvdecoder
)) {
124 swfdec_net_stream_onstatus (stream
, SWFDEC_AS_STR_NetStream_Play_Stop
, SWFDEC_AS_STR_status
);
126 stream
->buffering
= TRUE
;
127 swfdec_net_stream_onstatus (stream
, SWFDEC_AS_STR_NetStream_Buffer_Empty
,
128 SWFDEC_AS_STR_status
);
130 swfdec_net_stream_update_playing (stream
);
132 if (process_events
) {
133 while (process_events_from
<= stream
->current_time
) {
134 SwfdecAsValue name
, value
;
136 SwfdecBuffer
*event
= swfdec_flv_decoder_get_data (stream
->flvdecoder
, process_events_from
, &process_events_from
);
139 SWFDEC_LOG ("processing event from timestamp %u", process_events_from
);
140 process_events_from
++; /* increase so we get the next event next time */
141 swfdec_bits_init (&bits
, event
);
142 if (swfdec_amf_parse (SWFDEC_AS_OBJECT (stream
)->context
, &bits
, 2,
143 SWFDEC_AMF_STRING
, &name
, SWFDEC_AMF_MIXED_ARRAY
, &value
) != 2) {
144 SWFDEC_ERROR ("could not parse data tag");
146 swfdec_as_object_call (SWFDEC_AS_OBJECT (stream
),
147 SWFDEC_AS_VALUE_GET_STRING (&name
), 1, &value
, NULL
);
154 swfdec_net_stream_timeout (SwfdecTimeout
*timeout
)
156 SwfdecNetStream
*stream
= SWFDEC_NET_STREAM ((guchar
*) timeout
- G_STRUCT_OFFSET (SwfdecNetStream
, timeout
));
158 SWFDEC_LOG ("timeout fired");
159 stream
->timeout
.callback
= NULL
;
160 swfdec_net_stream_video_goto (stream
, stream
->next_time
);
161 if (stream
->next_time
> stream
->current_time
) {
162 SWFDEC_LOG ("readding timeout");
163 stream
->timeout
.timestamp
+= SWFDEC_MSECS_TO_TICKS (stream
->next_time
- stream
->current_time
);
164 stream
->timeout
.callback
= swfdec_net_stream_timeout
;
165 swfdec_player_add_timeout (SWFDEC_PLAYER (SWFDEC_AS_OBJECT (stream
)->context
), &stream
->timeout
);
168 /* FIXME: just unref and let it take care of removing itself? */
169 SWFDEC_LOG ("stopping audio due to EOS");
170 swfdec_audio_remove (stream
->audio
);
171 g_object_unref (stream
->audio
);
172 stream
->audio
= NULL
;
178 swfdec_net_stream_update_playing (SwfdecNetStream
*stream
)
180 SwfdecPlayer
*player
= SWFDEC_PLAYER (SWFDEC_AS_OBJECT (stream
)->context
);
181 gboolean should_play
;
183 should_play
= stream
->playing
; /* checks user-set play/pause */
184 should_play
&= !stream
->buffering
; /* checks enough data is available */
185 should_play
&= stream
->flvdecoder
!= NULL
; /* checks there even is something to play */
186 should_play
&= stream
->next_time
> stream
->current_time
; /* checks if EOF */
187 if (should_play
&& stream
->timeout
.callback
== NULL
) {
188 SWFDEC_DEBUG ("starting playback");
189 stream
->timeout
.callback
= swfdec_net_stream_timeout
;
190 stream
->timeout
.timestamp
= player
->time
+ SWFDEC_MSECS_TO_TICKS (stream
->next_time
- stream
->current_time
);
191 swfdec_player_add_timeout (player
, &stream
->timeout
);
192 if (stream
->flvdecoder
->audio
) {
193 g_assert (stream
->audio
== NULL
);
194 SWFDEC_LOG ("starting audio");
195 stream
->audio
= swfdec_audio_flv_new (player
,
196 stream
->flvdecoder
, stream
->current_time
);
198 SWFDEC_LOG ("no audio");
200 } else if (!should_play
&& stream
->timeout
.callback
!= NULL
) {
202 SWFDEC_LOG ("stopping audio");
203 swfdec_audio_remove (stream
->audio
);
204 g_object_unref (stream
->audio
);
205 stream
->audio
= NULL
;
207 swfdec_player_remove_timeout (player
, &stream
->timeout
);
208 stream
->timeout
.callback
= NULL
;
209 SWFDEC_DEBUG ("stopping playback");
213 /*** SWFDEC_LOADER_TARGET interface ***/
215 static SwfdecPlayer
*
216 swfdec_net_stream_loader_target_get_player (SwfdecLoaderTarget
*target
)
218 return SWFDEC_PLAYER (SWFDEC_AS_OBJECT (target
)->context
);
222 swfdec_net_stream_loader_target_error (SwfdecLoaderTarget
*target
,
223 SwfdecLoader
*loader
)
225 SwfdecNetStream
*stream
= SWFDEC_NET_STREAM (target
);
227 if (stream
->flvdecoder
== NULL
)
228 swfdec_net_stream_onstatus (stream
, SWFDEC_AS_STR_NetStream_Play_StreamNotFound
,
229 SWFDEC_AS_STR_error
);
233 swfdec_net_stream_loader_target_recheck (SwfdecNetStream
*stream
)
235 if (stream
->buffering
) {
237 if (swfdec_flv_decoder_get_video_info (stream
->flvdecoder
, &first
, &last
)) {
238 guint current
= MAX (first
, stream
->current_time
);
239 if (current
+ stream
->buffer_time
<= last
) {
240 swfdec_net_stream_video_goto (stream
, current
);
241 stream
->buffering
= FALSE
;
242 swfdec_net_stream_onstatus (stream
, SWFDEC_AS_STR_NetStream_Buffer_Full
,
243 SWFDEC_AS_STR_status
);
246 SWFDEC_ERROR ("no video stream, how do we update buffering?");
249 swfdec_net_stream_update_playing (stream
);
253 swfdec_net_stream_loader_target_parse (SwfdecLoaderTarget
*target
,
254 SwfdecLoader
*loader
)
256 SwfdecNetStream
*stream
= SWFDEC_NET_STREAM (target
);
257 SwfdecDecoderClass
*klass
;
258 gboolean recheck
= FALSE
;
260 if (loader
->state
!= SWFDEC_LOADER_STATE_EOF
&& swfdec_buffer_queue_get_depth (loader
->queue
) == 0) {
261 SWFDEC_INFO ("nothing to do");
264 if (stream
->flvdecoder
== NULL
) {
265 /* FIXME: add mp3 support */
266 stream
->flvdecoder
= g_object_new (SWFDEC_TYPE_FLV_DECODER
, NULL
);
267 SWFDEC_DECODER (stream
->flvdecoder
)->player
= SWFDEC_PLAYER (SWFDEC_AS_OBJECT (stream
)->context
);
268 SWFDEC_DECODER (stream
->flvdecoder
)->queue
= loader
->queue
;
269 swfdec_net_stream_onstatus (stream
, SWFDEC_AS_STR_NetStream_Play_Start
,
270 SWFDEC_AS_STR_status
);
271 swfdec_loader_set_data_type (loader
, SWFDEC_LOADER_DATA_FLV
);
273 klass
= SWFDEC_DECODER_GET_CLASS (stream
->flvdecoder
);
274 g_return_if_fail (klass
->parse
);
277 SwfdecStatus status
= klass
->parse (SWFDEC_DECODER (stream
->flvdecoder
));
279 case SWFDEC_STATUS_OK
:
281 case SWFDEC_STATUS_INIT
:
282 /* HACK for native flv playback */
283 swfdec_player_initialize (SWFDEC_PLAYER (SWFDEC_AS_OBJECT (stream
)->context
), 7,
284 SWFDEC_DECODER (stream
->flvdecoder
)->rate
,
285 SWFDEC_DECODER (stream
->flvdecoder
)->width
,
286 SWFDEC_DECODER (stream
->flvdecoder
)->height
);
287 case SWFDEC_STATUS_IMAGE
:
290 case SWFDEC_STATUS_ERROR
:
291 case SWFDEC_STATUS_NEEDBITS
:
292 case SWFDEC_STATUS_EOF
:
295 g_assert_not_reached ();
301 swfdec_net_stream_loader_target_recheck (stream
);
305 swfdec_net_stream_loader_target_eof (SwfdecLoaderTarget
*target
,
306 SwfdecLoader
*loader
)
308 SwfdecNetStream
*stream
= SWFDEC_NET_STREAM (target
);
311 swfdec_flv_decoder_eof (stream
->flvdecoder
);
312 swfdec_net_stream_onstatus (stream
, SWFDEC_AS_STR_NetStream_Buffer_Flush
,
313 SWFDEC_AS_STR_status
);
314 swfdec_net_stream_video_goto (stream
, stream
->current_time
);
315 stream
->buffering
= FALSE
;
316 if (swfdec_flv_decoder_get_video_info (stream
->flvdecoder
, &first
, &last
) &&
317 stream
->current_time
+ stream
->buffer_time
<= last
) {
318 swfdec_net_stream_onstatus (stream
, SWFDEC_AS_STR_NetStream_Buffer_Full
,
319 SWFDEC_AS_STR_status
);
321 swfdec_net_stream_loader_target_recheck (stream
);
325 swfdec_net_stream_loader_target_init (SwfdecLoaderTargetInterface
*iface
)
327 iface
->get_player
= swfdec_net_stream_loader_target_get_player
;
328 iface
->parse
= swfdec_net_stream_loader_target_parse
;
329 iface
->eof
= swfdec_net_stream_loader_target_eof
;
330 iface
->error
= swfdec_net_stream_loader_target_error
;
333 /*** SWFDEC VIDEO MOVIE INPUT ***/
336 swfdec_net_stream_input_connect (SwfdecVideoMovieInput
*input
, SwfdecVideoMovie
*movie
)
338 SwfdecNetStream
*stream
= SWFDEC_NET_STREAM ((guchar
*) input
- G_STRUCT_OFFSET (SwfdecNetStream
, input
));
340 stream
->movies
= g_list_prepend (stream
->movies
, movie
);
341 g_object_ref (stream
);
345 swfdec_net_stream_input_disconnect (SwfdecVideoMovieInput
*input
, SwfdecVideoMovie
*movie
)
347 SwfdecNetStream
*stream
= SWFDEC_NET_STREAM ((guchar
*) input
- G_STRUCT_OFFSET (SwfdecNetStream
, input
));
349 stream
->movies
= g_list_remove (stream
->movies
, movie
);
350 g_object_unref (stream
);
353 /*** SWFDEC_NET_STREAM ***/
355 G_DEFINE_TYPE_WITH_CODE (SwfdecNetStream
, swfdec_net_stream
, SWFDEC_TYPE_AS_OBJECT
,
356 G_IMPLEMENT_INTERFACE (SWFDEC_TYPE_LOADER_TARGET
, swfdec_net_stream_loader_target_init
))
359 swfdec_net_stream_dispose (GObject
*object
)
361 SwfdecNetStream
*stream
= SWFDEC_NET_STREAM (object
);
363 swfdec_net_stream_set_playing (stream
, FALSE
);
364 if (stream
->surface
) {
365 cairo_surface_destroy (stream
->surface
);
366 stream
->surface
= NULL
;
368 if (stream
->decoder
) {
369 swfdec_video_decoder_free (stream
->decoder
);
370 stream
->decoder
= NULL
;
372 swfdec_net_stream_set_loader (stream
, NULL
);
373 g_assert (stream
->movies
== NULL
);
375 G_OBJECT_CLASS (swfdec_net_stream_parent_class
)->dispose (object
);
379 swfdec_net_stream_get_variable (SwfdecAsObject
*object
, SwfdecAsObject
*orig
,
380 const char *variable
, SwfdecAsValue
*val
, guint
*flags
)
382 SwfdecNetStream
*stream
;
384 if (SWFDEC_AS_OBJECT_CLASS (swfdec_net_stream_parent_class
)->get (object
, orig
, variable
, val
, flags
))
387 stream
= SWFDEC_NET_STREAM (object
);
388 /* FIXME: need case insensitive comparisons? */
389 if (variable
== SWFDEC_AS_STR_time
) {
391 if (stream
->flvdecoder
== NULL
||
392 !swfdec_flv_decoder_get_video_info (stream
->flvdecoder
, &msecs
, NULL
)) {
393 SWFDEC_AS_VALUE_SET_INT (val
, 0);
395 if (msecs
>= stream
->current_time
)
398 msecs
= stream
->current_time
- msecs
;
399 SWFDEC_AS_VALUE_SET_NUMBER (val
, msecs
/ 1000.);
403 } else if (variable
== SWFDEC_AS_STR_bytesLoaded
) {
404 if (stream
->loader
== NULL
)
405 SWFDEC_AS_VALUE_SET_INT (val
, 0);
407 SWFDEC_AS_VALUE_SET_INT (val
, swfdec_loader_get_loaded (stream
->loader
));
410 } else if (variable
== SWFDEC_AS_STR_bytesTotal
) {
412 if (stream
->loader
== NULL
) {
415 bytes
= swfdec_loader_get_size (stream
->loader
);
417 bytes
= swfdec_loader_get_loaded (stream
->loader
);
419 SWFDEC_AS_VALUE_SET_INT (val
, bytes
);
427 swfdec_net_stream_mark (SwfdecAsObject
*object
)
429 SwfdecNetStream
*stream
= SWFDEC_NET_STREAM (object
);
432 swfdec_as_object_mark (SWFDEC_AS_OBJECT (stream
->conn
));
434 SWFDEC_AS_OBJECT_CLASS (swfdec_net_stream_parent_class
)->mark (object
);
438 swfdec_net_stream_class_init (SwfdecNetStreamClass
*klass
)
440 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
441 SwfdecAsObjectClass
*asobject_class
= SWFDEC_AS_OBJECT_CLASS (klass
);
443 object_class
->dispose
= swfdec_net_stream_dispose
;
445 asobject_class
->get
= swfdec_net_stream_get_variable
;
446 asobject_class
->mark
= swfdec_net_stream_mark
;
450 swfdec_net_stream_init (SwfdecNetStream
*stream
)
452 stream
->input
.connect
= swfdec_net_stream_input_connect
;
453 stream
->input
.disconnect
= swfdec_net_stream_input_disconnect
;
455 stream
->buffer_time
= 100; /* msecs */
459 swfdec_net_stream_new (SwfdecNetConnection
*conn
)
461 SwfdecAsContext
*context
;
462 SwfdecNetStream
*stream
;
464 g_return_val_if_fail (SWFDEC_IS_NET_CONNECTION (conn
), NULL
);
466 context
= SWFDEC_AS_OBJECT (conn
)->context
;
467 if (!swfdec_as_context_use_mem (context
, sizeof (SwfdecNetStream
)))
469 stream
= g_object_new (SWFDEC_TYPE_NET_STREAM
, NULL
);
470 swfdec_as_object_add (SWFDEC_AS_OBJECT (stream
), context
, sizeof (SwfdecNetStream
));
477 swfdec_net_stream_set_url (SwfdecNetStream
*stream
, const char *url
)
479 SwfdecLoader
*loader
;
481 g_return_if_fail (SWFDEC_IS_NET_STREAM (stream
));
482 g_return_if_fail (url
!= NULL
);
484 /* FIXME: use the connection once connections are implemented */
485 loader
= swfdec_player_load (SWFDEC_PLAYER (SWFDEC_AS_OBJECT (stream
)->context
), url
);
486 swfdec_net_stream_set_loader (stream
, loader
);
487 g_object_unref (loader
);
491 swfdec_net_stream_set_loader (SwfdecNetStream
*stream
, SwfdecLoader
*loader
)
493 g_return_if_fail (SWFDEC_IS_NET_STREAM (stream
));
494 g_return_if_fail (loader
== NULL
|| SWFDEC_IS_LOADER (loader
));
496 if (stream
->loader
) {
497 swfdec_loader_set_target (stream
->loader
, NULL
);
498 swfdec_loader_close (stream
->loader
);
499 g_object_unref (stream
->loader
);
501 if (stream
->flvdecoder
) {
502 g_object_unref (stream
->flvdecoder
);
503 stream
->flvdecoder
= NULL
;
505 stream
->loader
= loader
;
506 stream
->buffering
= TRUE
;
508 g_object_ref (loader
);
509 swfdec_loader_set_target (loader
, SWFDEC_LOADER_TARGET (stream
));
511 swfdec_net_stream_set_playing (stream
, TRUE
);
515 swfdec_net_stream_set_playing (SwfdecNetStream
*stream
, gboolean playing
)
517 g_return_if_fail (SWFDEC_IS_NET_STREAM (stream
));
519 stream
->playing
= playing
;
521 swfdec_net_stream_update_playing (stream
);
525 swfdec_net_stream_get_playing (SwfdecNetStream
*stream
)
527 g_return_val_if_fail (SWFDEC_IS_NET_STREAM (stream
), FALSE
);
529 return stream
->playing
;
533 swfdec_net_stream_set_buffer_time (SwfdecNetStream
*stream
, double secs
)
535 g_return_if_fail (SWFDEC_IS_NET_STREAM (stream
));
537 /* FIXME: is this correct? */
541 stream
->buffer_time
= secs
* 1000;
545 swfdec_net_stream_get_buffer_time (SwfdecNetStream
*stream
)
547 g_return_val_if_fail (SWFDEC_IS_NET_STREAM (stream
), 0.1);
549 return (double) stream
->buffer_time
/ 1000.0;
553 swfdec_net_stream_seek (SwfdecNetStream
*stream
, double secs
)
555 guint first
, last
, msecs
;
557 g_return_if_fail (SWFDEC_IS_NET_STREAM (stream
));
559 if (stream
->flvdecoder
== NULL
)
561 if (!isfinite (secs
) || secs
< 0) {
562 SWFDEC_ERROR ("seeking to %g doesn't work", secs
);
565 if (!swfdec_flv_decoder_get_video_info (stream
->flvdecoder
, &first
, &last
)) {
566 SWFDEC_ERROR ("FIXME: implement seeking in audio only NetStream");
573 swfdec_flv_decoder_get_video (stream
->flvdecoder
, msecs
, TRUE
, NULL
, &msecs
, NULL
);
574 swfdec_net_stream_video_goto (stream
, msecs
);
575 /* FIXME: this needs to be implemented correctly, but requires changes to audio handling:
576 * - creating a new audio stream will cause attachAudio scripts to lose information
577 * - implementing seek on audio stream requires a SwfdecAudio::changed signal so audio
578 * backends can react correctly.
581 SWFDEC_WARNING ("FIXME: restarting audio after seek");
582 swfdec_audio_remove (stream
->audio
);
583 g_object_unref (stream
->audio
);
584 stream
->audio
= swfdec_audio_flv_new (SWFDEC_PLAYER (SWFDEC_AS_OBJECT (stream
)->context
),
585 stream
->flvdecoder
, stream
->current_time
);