rework video handling
[swfdec.git] / libswfdec / swfdec_net_stream.c
blob3ffe057ce9afe0964134fedd1fb903b247075b11
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 cairo_surface_t *
54 swfdec_net_stream_decode_video (SwfdecNetStream *stream, SwfdecBuffer *buffer)
56 SwfdecVideoDecoder *decoder = stream->decoder;
57 cairo_surface_t *surface;
59 if (decoder == NULL)
60 return NULL;
62 if (decoder->codec == SWFDEC_VIDEO_CODEC_VP6 ||
63 decoder->codec == SWFDEC_VIDEO_CODEC_VP6_ALPHA) {
64 guint wsub, hsub;
65 SwfdecBuffer *tmp;
66 wsub = *buffer->data;
67 hsub = wsub & 0xF;
68 wsub >>= 4;
69 tmp = swfdec_buffer_new_subbuffer (buffer, 1, buffer->length - 1);
70 surface = swfdec_video_decoder_decode (decoder, tmp);
71 swfdec_buffer_unref (tmp);
72 if (hsub || wsub) {
73 SWFDEC_FIXME ("need to subtract %ux%u pixels", wsub, hsub);
75 } else {
76 surface = swfdec_video_decoder_decode (decoder, buffer);
78 return surface;
81 static void swfdec_net_stream_update_playing (SwfdecNetStream *stream);
82 static void
83 swfdec_net_stream_video_goto (SwfdecNetStream *stream, guint timestamp)
85 SwfdecBuffer *buffer;
86 SwfdecVideoCodec format;
87 cairo_surface_t *old;
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);
102 } else {
103 buffer = NULL;
105 if (buffer == NULL) {
106 SWFDEC_ERROR ("got no buffer - no video available?");
107 } else {
108 if (format != stream->format) {
109 if (stream->decoder)
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) {
116 GList *walk;
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);
125 } else {
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;
135 SwfdecBits bits;
136 SwfdecBuffer *event = swfdec_flv_decoder_get_data (stream->flvdecoder, process_events_from, &process_events_from);
137 if (!event)
138 break;
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");
145 } else {
146 swfdec_as_object_call (SWFDEC_AS_OBJECT (stream),
147 SWFDEC_AS_VALUE_GET_STRING (&name), 1, &value, NULL);
153 static void
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);
166 } else {
167 if (stream->audio) {
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;
177 static void
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);
197 } else {
198 SWFDEC_LOG ("no audio");
200 } else if (!should_play && stream->timeout.callback != NULL) {
201 if (stream->audio) {
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);
221 static void
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);
232 static void
233 swfdec_net_stream_loader_target_recheck (SwfdecNetStream *stream)
235 if (stream->buffering) {
236 guint first, last;
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);
245 } else {
246 SWFDEC_ERROR ("no video stream, how do we update buffering?");
249 swfdec_net_stream_update_playing (stream);
252 static void
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");
262 return;
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);
276 while (TRUE) {
277 SwfdecStatus status = klass->parse (SWFDEC_DECODER (stream->flvdecoder));
278 switch (status) {
279 case SWFDEC_STATUS_OK:
280 break;
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:
288 recheck = TRUE;
289 break;
290 case SWFDEC_STATUS_ERROR:
291 case SWFDEC_STATUS_NEEDBITS:
292 case SWFDEC_STATUS_EOF:
293 goto out;
294 default:
295 g_assert_not_reached ();
296 return;
299 out:
300 if (recheck)
301 swfdec_net_stream_loader_target_recheck (stream);
304 static void
305 swfdec_net_stream_loader_target_eof (SwfdecLoaderTarget *target,
306 SwfdecLoader *loader)
308 SwfdecNetStream *stream = SWFDEC_NET_STREAM (target);
309 guint first, last;
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);
324 static void
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 ***/
335 static void
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);
344 static void
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))
358 static void
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);
378 static gboolean
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))
385 return TRUE;
387 stream = SWFDEC_NET_STREAM (object);
388 /* FIXME: need case insensitive comparisons? */
389 if (variable == SWFDEC_AS_STR_time) {
390 guint msecs;
391 if (stream->flvdecoder == NULL ||
392 !swfdec_flv_decoder_get_video_info (stream->flvdecoder, &msecs, NULL)) {
393 SWFDEC_AS_VALUE_SET_INT (val, 0);
394 } else {
395 if (msecs >= stream->current_time)
396 msecs = 0;
397 else
398 msecs = stream->current_time - msecs;
400 SWFDEC_AS_VALUE_SET_NUMBER (val, msecs / 1000.);
401 *flags = 0;
402 return TRUE;
403 } else if (variable == SWFDEC_AS_STR_bytesLoaded) {
404 if (stream->loader == NULL)
405 SWFDEC_AS_VALUE_SET_INT (val, 0);
406 else
407 SWFDEC_AS_VALUE_SET_INT (val, swfdec_loader_get_loaded (stream->loader));
408 *flags = 0;
409 return TRUE;
410 } else if (variable == SWFDEC_AS_STR_bytesTotal) {
411 guint bytes;
412 if (stream->loader == NULL) {
413 bytes = 0;
414 } else {
415 bytes = swfdec_loader_get_size (stream->loader);
416 if (bytes == 0)
417 bytes = swfdec_loader_get_loaded (stream->loader);
419 SWFDEC_AS_VALUE_SET_INT (val, bytes);
420 *flags = 0;
421 return TRUE;
423 return FALSE;
426 static void
427 swfdec_net_stream_mark (SwfdecAsObject *object)
429 SwfdecNetStream *stream = SWFDEC_NET_STREAM (object);
431 if (stream->conn)
432 swfdec_as_object_mark (SWFDEC_AS_OBJECT (stream->conn));
434 SWFDEC_AS_OBJECT_CLASS (swfdec_net_stream_parent_class)->mark (object);
437 static void
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;
449 static void
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 */
458 SwfdecNetStream *
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)))
468 return NULL;
469 stream = g_object_new (SWFDEC_TYPE_NET_STREAM, NULL);
470 swfdec_as_object_add (SWFDEC_AS_OBJECT (stream), context, sizeof (SwfdecNetStream));
471 stream->conn = conn;
473 return stream;
476 void
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);
490 void
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;
507 if (loader) {
508 g_object_ref (loader);
509 swfdec_loader_set_target (loader, SWFDEC_LOADER_TARGET (stream));
511 swfdec_net_stream_set_playing (stream, TRUE);
514 void
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);
524 gboolean
525 swfdec_net_stream_get_playing (SwfdecNetStream *stream)
527 g_return_val_if_fail (SWFDEC_IS_NET_STREAM (stream), FALSE);
529 return stream->playing;
532 void
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? */
538 if (secs <= 0)
539 return;
541 stream->buffer_time = secs * 1000;
544 double
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;
552 void
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)
560 return;
561 if (!isfinite (secs) || secs < 0) {
562 SWFDEC_ERROR ("seeking to %g doesn't work", secs);
563 return;
565 if (!swfdec_flv_decoder_get_video_info (stream->flvdecoder, &first, &last)) {
566 SWFDEC_ERROR ("FIXME: implement seeking in audio only NetStream");
567 return;
569 msecs = secs * 1000;
570 msecs += first;
571 if (msecs > last)
572 msecs = last;
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.
580 if (stream->audio) {
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);