From 1dae961cbffa39f488d7b5bd766ca5f846dc7481 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 31 Mar 2005 09:43:49 +0000 Subject: [PATCH] Plugin port to 0.9, ogg/theora playback should work in the seek example now. Original commit message from CVS: Plugin port to 0.9, ogg/theora playback should work in the seek example now. Removed old examples. Removed old oggvorbisenc, renamed rawvorbisenc to vorbisenc as explained in 0.9 TODO doc. --- ChangeLog | 159 + common | 2 +- examples/seeking/Makefile.am | 2 +- examples/seeking/cdparanoia.c | 11 +- examples/seeking/cdplayer.c | 39 +- examples/seeking/playbin.c | 270 -- examples/seeking/seek.c | 450 ++- examples/seeking/spider_seek.c | 379 --- examples/seeking/vorbisfile.c | 266 -- ext/gnomevfs/Makefile.am | 5 +- ext/gnomevfs/gstgnomevfs.c | 7 +- ext/gnomevfs/gstgnomevfssrc.c | 473 +-- ext/ogg/README | 105 +- ext/ogg/gstoggdemux.c | 3212 +++++++++++--------- ext/ogg/gstoggmux.c | 58 +- ext/ogg/gstogmparse.c | 34 +- ext/theora/theoradec.c | 224 +- ext/theora/theoraenc.c | 119 +- ext/vorbis/Makefile.am | 4 +- ext/vorbis/oggvorbisenc.c | 1007 ------ ext/vorbis/oggvorbisenc.h | 100 - ext/vorbis/vorbis.c | 8 +- ext/vorbis/vorbisdec.c | 168 +- ext/vorbis/vorbisenc.c | 171 +- ext/vorbis/vorbisenc.h | 2 - ext/vorbis/vorbisparse.c | 19 +- gst-libs/gst/audio/audioclock.c | 4 +- gst-libs/gst/audio/gstaudiofilter.c | 39 +- gst-libs/gst/audio/testchannels.c | 2 +- gst-libs/gst/gconf/gconf.c | 23 +- gst-libs/gst/media-info/media-info-priv.c | 21 +- gst-libs/gst/media-info/media-info.c | 17 +- gst-libs/gst/play/play.c | 22 +- gst-libs/gst/riff/riff-read.c | 1008 ++---- gst-libs/gst/riff/riff-read.h | 126 +- gst-libs/gst/riff/riff.c | 6 +- gst-libs/gst/video/Makefile.am | 2 +- gst-libs/gst/video/gstvideosink.c | 16 +- gst-libs/gst/video/gstvideosink.h | 13 +- gst-libs/gst/video/videosink.h | 13 +- gst/audioconvert/bufferframesconvert.c | 55 +- gst/audioconvert/channelmixtest.c | 4 +- gst/audioconvert/gstaudioconvert.c | 249 +- gst/audioconvert/gstchannelmix.h | 3 + gst/ffmpegcolorspace/avcodec.h | 1 - gst/ffmpegcolorspace/gstffmpegcolorspace.c | 217 +- gst/tags/gstid3tag.c | 1 - gst/tags/gstvorbistag.c | 41 +- gst/typefind/gsttypefindfunctions.c | 15 +- gst/videotestsrc/gstvideotestsrc.c | 289 +- sys/xvimage/xvimagesink.c | 273 +- sys/xvimage/xvimagesink.h | 6 - tests/examples/seek/Makefile.am | 2 +- tests/examples/seek/playbin.c | 270 -- tests/examples/seek/seek.c | 450 ++- tests/examples/seek/spider_seek.c | 379 --- tests/examples/seek/vorbisfile.c | 266 -- tests/old/examples/seek/Makefile.am | 2 +- tests/old/examples/seek/cdparanoia.c | 11 +- tests/old/examples/seek/cdplayer.c | 39 +- tests/old/examples/seek/playbin.c | 270 -- tests/old/examples/seek/spider_seek.c | 379 --- tests/old/examples/seek/vorbisfile.c | 266 -- 63 files changed, 4510 insertions(+), 7584 deletions(-) delete mode 100644 examples/seeking/playbin.c delete mode 100644 examples/seeking/spider_seek.c delete mode 100644 examples/seeking/vorbisfile.c delete mode 100644 ext/vorbis/oggvorbisenc.c delete mode 100644 ext/vorbis/oggvorbisenc.h delete mode 100644 tests/examples/seek/playbin.c delete mode 100644 tests/examples/seek/spider_seek.c delete mode 100644 tests/examples/seek/vorbisfile.c delete mode 100644 tests/old/examples/seek/playbin.c delete mode 100644 tests/old/examples/seek/spider_seek.c delete mode 100644 tests/old/examples/seek/vorbisfile.c diff --git a/ChangeLog b/ChangeLog index abca8b246a..7969c1d0ea 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,162 @@ +2005-03-31 Wim Taymans + + * examples/seeking/Makefile.am: + * examples/seeking/cdparanoia.c: (main): + * examples/seeking/cdplayer.c: (update_scale), (stop_seek), + (play_cb), (pause_cb), (stop_cb), (main): + * examples/seeking/playbin.c: + * examples/seeking/seek.c: (dynamic_link), (make_mod_pipeline), + (make_dv_pipeline), (make_wav_pipeline), (make_flac_pipeline), + (make_sid_pipeline), (make_vorbis_pipeline), + (make_theora_pipeline), (make_vorbis_theora_pipeline), + (make_avi_msmpeg4v3_mp3_pipeline), (make_mp3_pipeline), + (make_avi_pipeline), (make_mpeg_pipeline), (make_mpegnt_pipeline), + (make_playerbin_pipeline), (update_scale), (end_scrub), (do_seek), + (seek_cb), (start_seek), (stop_seek), (play_cb), (pause_cb), + (stop_cb), (main): + * examples/seeking/spider_seek.c: + * examples/seeking/vorbisfile.c: + * ext/gnomevfs/Makefile.am: + * ext/gnomevfs/gstgnomevfs.c: (plugin_init): + * ext/gnomevfs/gstgnomevfssrc.c: (gst_gnomevfssrc_base_init), + (gst_gnomevfssrc_class_init), (gst_gnomevfssrc_init), + (gst_gnomevfssrc_get_property), (gst_gnomevfssrc_get), + (gst_gnomevfssrc_open_file), (gst_gnomevfssrc_close_file), + (gst_gnomevfssrc_getrange), (gst_gnomevfssrc_loop), + (gst_gnomevfssrc_activate), (gst_gnomevfssrc_change_state), + (gst_gnomevfssrc_srcpad_query), (gst_gnomevfssrc_srcpad_event): + * ext/ogg/README: + * ext/ogg/gstoggdemux.c: (gst_ogg_pad_get_type), + (gst_ogg_pad_class_init), (gst_ogg_pad_init), + (gst_ogg_pad_dispose), (gst_ogg_pad_finalize), + (gst_ogg_pad_formats), (gst_ogg_pad_event_masks), + (gst_ogg_pad_query_types), (gst_ogg_pad_getcaps), + (gst_ogg_pad_src_convert), (gst_ogg_pad_src_query), + (gst_ogg_pad_event), (gst_ogg_pad_reset), + (gst_ogg_demux_factory_filter), (compare_ranks), + (gst_ogg_pad_internal_chain), (gst_ogg_pad_typefind), + (gst_ogg_pad_submit_packet), (gst_ogg_pad_submit_page), + (gst_ogg_chain_new), (gst_ogg_chain_free), + (gst_ogg_chain_new_stream), (gst_ogg_chain_get_stream), + (gst_ogg_chain_has_stream), (gst_ogg_demux_base_init), + (gst_ogg_demux_class_init), (gst_ogg_demux_init), + (gst_ogg_demux_finalize), (gst_ogg_demux_handle_event), + (gst_ogg_demux_submit_buffer), (gst_ogg_demux_seek), + (gst_ogg_demux_get_data), (gst_ogg_demux_get_next_page), + (gst_ogg_demux_get_prev_page), + (gst_ogg_demux_deactivate_current_chain), + (gst_ogg_demux_activate_chain), (gst_ogg_demux_perform_seek), + (gst_ogg_demux_bisect_forward_serialno), + (gst_ogg_demux_read_chain), (gst_ogg_demux_read_end_chain), + (gst_ogg_demux_find_pad), (gst_ogg_demux_find_chain), + (gst_ogg_demux_find_chains), (gst_ogg_demux_chain_unlocked), + (gst_ogg_demux_chain), (gst_ogg_demux_send_eos), + (gst_ogg_demux_loop), (gst_ogg_demux_sink_activate), + (gst_ogg_demux_change_state), (gst_ogg_print): + * ext/ogg/gstoggmux.c: (gst_ogg_mux_class_init), + (gst_ogg_mux_init), (gst_ogg_mux_sinkconnect), + (gst_ogg_mux_next_buffer), (gst_ogg_mux_buffer_from_page), + (gst_ogg_mux_push_page), (gst_ogg_mux_send_headers), + (gst_ogg_mux_loop): + * ext/ogg/gstogmparse.c: (gst_ogm_parse_chain): + * ext/theora/theoradec.c: (gst_theora_dec_init), (_inc_granulepos), + (theora_dec_src_convert), (theora_dec_sink_convert), + (theora_dec_src_query), (theora_dec_src_event), + (theora_dec_sink_event), (theora_dec_chain), + (theora_dec_change_state): + * ext/theora/theoraenc.c: (gst_theora_enc_init), + (theora_enc_sink_setcaps), (theora_buffer_from_packet), + (theora_push_buffer), (theora_enc_sink_event), (theora_enc_chain), + (theora_enc_change_state): + * ext/vorbis/Makefile.am: + * ext/vorbis/oggvorbisenc.c: + * ext/vorbis/oggvorbisenc.h: + * ext/vorbis/vorbis.c: (plugin_init): + * ext/vorbis/vorbisdec.c: (gst_vorbis_dec_init), + (vorbis_dec_src_query), (vorbis_dec_src_event), + (vorbis_dec_sink_event), (vorbis_dec_chain), + (vorbis_dec_change_state): + * ext/vorbis/vorbisenc.c: (gst_vorbisenc_class_init), + (gst_vorbisenc_sink_setcaps), (gst_vorbisenc_init), + (gst_vorbisenc_buffer_from_packet), (gst_vorbisenc_push_buffer), + (gst_vorbisenc_sink_event), (gst_vorbisenc_chain), + (gst_vorbisenc_change_state): + * ext/vorbis/vorbisenc.h: + * ext/vorbis/vorbisparse.c: (vorbis_parse_chain): + * gst-libs/gst/audio/audioclock.c: + * gst-libs/gst/audio/gstaudiofilter.c: (gst_audiofilter_link), + (gst_audiofilter_init), (gst_audiofilter_chain): + * gst-libs/gst/audio/testchannels.c: (main): + * gst-libs/gst/gconf/gconf.c: (gst_bin_find_unconnected_pad): + * gst-libs/gst/media-info/media-info-priv.c: (gmip_reset), + (gmip_find_type), (gmip_find_stream), (gmip_find_track_metadata), + (gmip_find_track_streaminfo), (gmip_find_track_format): + * gst-libs/gst/media-info/media-info.c: + (gst_media_info_read_idler): + * gst-libs/gst/play/play.c: (gst_play_get_sink_element), + (gst_play_get_all_by_interface): + * gst-libs/gst/riff/riff-read.c: (gst_riff_read_chunk), + (gst_riff_parse_chunk), (gst_riff_parse_file_header), + (gst_riff_parse_strh), (gst_riff_parse_strf_vids), + (gst_riff_parse_strf_auds), (gst_riff_parse_strf_iavs), + (gst_riff_parse_info): + * gst-libs/gst/riff/riff-read.h: + * gst-libs/gst/riff/riff.c: (plugin_init): + * gst-libs/gst/video/Makefile.am: + * gst-libs/gst/video/gstvideosink.c: (gst_videosink_init), + (gst_videosink_class_init), (gst_videosink_get_type): + * gst-libs/gst/video/videosink.h: + * gst/audioconvert/bufferframesconvert.c: + (buffer_frames_convert_init), (buffer_frames_convert_fixate), + (buffer_frames_convert_setcaps), (buffer_frames_convert_chain): + * gst/audioconvert/channelmixtest.c: (main): + * gst/audioconvert/gstaudioconvert.c: (gst_audio_convert_init), + (gst_audio_convert_chain), + (gst_audio_convert_caps_remove_format_info), + (gst_audio_convert_getcaps), (gst_audio_convert_parse_caps), + (gst_audio_convert_setcaps), (_fixate_caps_to_int), + (gst_audio_convert_fixate), (gst_audio_convert_get_buffer), + (gst_audio_convert_buffer_to_default_format), + (gst_audio_convert_buffer_from_default_format), + (gst_audio_convert_channels): + * gst/audioconvert/gstchannelmix.h: + * gst/ffmpegcolorspace/avcodec.h: + * gst/ffmpegcolorspace/gstffmpegcolorspace.c: + (gst_ffmpegcsp_caps_remove_format_info), (gst_ffmpegcsp_getcaps), + (gst_ffmpegcsp_configure_context), (gst_ffmpegcsp_setcaps), + (gst_ffmpegcsp_init), (gst_ffmpegcsp_chain): + * gst/tags/gstid3tag.c: (gst_tag_extract_id3v1_string): + * gst/tags/gstvorbistag.c: (gst_vorbis_tag_chain): + * gst/typefind/gsttypefindfunctions.c: (aac_type_find), + (mp3_type_find), (mpeg2_sys_type_find), (mpeg1_sys_type_find), + (mpeg_video_type_find), (mpeg_video_stream_type_find), + (dv_type_find): + * gst/videotestsrc/gstvideotestsrc.c: + (gst_videotestsrc_class_init), (gst_videotestsrc_src_negotiate), + (gst_videotestsrc_src_link), (gst_videotestsrc_parse_caps), + (gst_videotestsrc_src_accept_caps), (gst_videotestsrc_setcaps), + (gst_videotestsrc_src_unlink), (gst_videotestsrc_activate), + (gst_videotestsrc_change_state), (gst_videotestsrc_getcaps), + (gst_videotestsrc_init), (gst_videotestsrc_src_query), + (gst_videotestsrc_handle_src_event), (gst_videotestsrc_loop): + * sys/xvimage/xvimagesink.c: (gst_xvimagesink_get_xv_support), + (gst_xvimagesink_xcontext_clear), (gst_xvimagesink_fixate), + (gst_xvimagesink_getcaps), (gst_xvimagesink_setcaps), + (gst_xvimagesink_change_state), (gst_xvimagesink_get_times), + (gst_xvimagesink_show_frame), (gst_xvimagesink_chain), + (gst_xvimagesink_buffer_free), (gst_xvimagesink_buffer_alloc), + (gst_xvimagesink_navigation_send_event), + (gst_xvimagesink_set_xwindow_id), (gst_xvimagesink_expose), + (gst_xvimagesink_set_property), (gst_xvimagesink_finalize), + (gst_xvimagesink_init), (gst_xvimagesink_class_init): + * sys/xvimage/xvimagesink.h: + Plugin port to 0.9, ogg/theora playback should work in the seek + example now. + Removed old examples. + Removed old oggvorbisenc, renamed rawvorbisenc to vorbisenc as + explained in 0.9 TODO doc. + + 2005-02-23 Thomas Vander Stichele * autogen.sh: diff --git a/common b/common index b2638c1007..131c263212 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit b2638c100721f67b280c3b43b21f1ce1c9b5e316 +Subproject commit 131c2632127e6f061b5270d8f80651782a4fdd13 diff --git a/examples/seeking/Makefile.am b/examples/seeking/Makefile.am index 582306ac8a..3e7c7886db 100644 --- a/examples/seeking/Makefile.am +++ b/examples/seeking/Makefile.am @@ -1,4 +1,4 @@ -examples = seek spider_seek cdplayer cdparanoia vorbisfile playbin chained +examples = seek cdplayer cdparanoia noinst_PROGRAMS = $(examples) diff --git a/examples/seeking/cdparanoia.c b/examples/seeking/cdparanoia.c index d0e7bc94ba..f5b8c32631 100644 --- a/examples/seeking/cdparanoia.c +++ b/examples/seeking/cdparanoia.c @@ -159,7 +159,7 @@ main (int argc, char **argv) gst_element_link_pads (cdparanoia, "src", audiosink, "sink"); g_signal_connect (G_OBJECT (pipeline), "deep_notify", - G_CALLBACK (gst_element_default_deep_notify), NULL); + G_CALLBACK (gst_object_default_deep_notify), NULL); gst_element_set_state (pipeline, GST_STATE_PAUSED); @@ -184,10 +184,9 @@ main (int argc, char **argv) gst_element_set_state (pipeline, GST_STATE_PLAYING); count = 0; - while (gst_bin_iterate (GST_BIN (pipeline))) { + while (count++ < 500) { get_position_info (cdparanoia); - if (count++ > 500) - break; + g_usleep (G_USEC_PER_SEC / 2); } gst_element_set_state (pipeline, GST_STATE_PAUSED); @@ -202,8 +201,10 @@ main (int argc, char **argv) gst_element_set_state (pipeline, GST_STATE_PLAYING); - while (gst_bin_iterate (GST_BIN (pipeline))) { + count = 0; + while (count++ < 500) { get_position_info (cdparanoia); + g_usleep (G_USEC_PER_SEC / 2); } g_print ("\n"); diff --git a/examples/seeking/cdplayer.c b/examples/seeking/cdplayer.c index 21fa302d3f..190f17b029 100644 --- a/examples/seeking/cdplayer.c +++ b/examples/seeking/cdplayer.c @@ -125,7 +125,7 @@ update_scale (gpointer data) GstFormat format = GST_FORMAT_TIME; duration = 0; - clock = gst_bin_get_clock (GST_BIN (pipeline)); + clock = gst_pipeline_get_clock (GST_PIPELINE (pipeline)); if (seekable_elements) { GstElement *element = GST_ELEMENT (seekable_elements->data); @@ -148,20 +148,6 @@ update_scale (gpointer data) return TRUE; } -static gboolean -iterate (gpointer data) -{ - gboolean res = TRUE; - - g_print ("iterate\n"); - res = gst_bin_iterate (GST_BIN (data)); - if (!res) { - gtk_timeout_remove (update_id); - g_print ("stopping iterations\n"); - } - return res; -} - static gboolean start_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data) { @@ -194,8 +180,6 @@ stop_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data) } gst_element_set_state (pipeline, GST_STATE_PLAYING); - if (!GST_FLAG_IS_SET (pipeline, GST_BIN_SELF_SCHEDULABLE)) - gtk_idle_add ((GtkFunction) iterate, pipeline); update_id = gtk_timeout_add (UPDATE_INTERVAL, (GtkFunction) update_scale, pipeline); @@ -205,10 +189,11 @@ stop_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data) static void play_cb (GtkButton * button, gpointer data) { - if (gst_element_get_state (pipeline) != GST_STATE_PLAYING) { + GstElementState state; + + gst_element_get_state (pipeline, &state, NULL, NULL); + if (state != GST_STATE_PLAYING) { gst_element_set_state (pipeline, GST_STATE_PLAYING); - if (!GST_FLAG_IS_SET (pipeline, GST_BIN_SELF_SCHEDULABLE)) - gtk_idle_add ((GtkFunction) iterate, pipeline); update_id = gtk_timeout_add (UPDATE_INTERVAL, (GtkFunction) update_scale, pipeline); } @@ -217,7 +202,10 @@ play_cb (GtkButton * button, gpointer data) static void pause_cb (GtkButton * button, gpointer data) { - if (gst_element_get_state (pipeline) != GST_STATE_PAUSED) { + GstElementState state; + + gst_element_get_state (pipeline, &state, NULL, NULL); + if (state != GST_STATE_PAUSED) { gst_element_set_state (pipeline, GST_STATE_PAUSED); gtk_timeout_remove (update_id); } @@ -226,7 +214,10 @@ pause_cb (GtkButton * button, gpointer data) static void stop_cb (GtkButton * button, gpointer data) { - if (gst_element_get_state (pipeline) != GST_STATE_READY) { + GstElementState state; + + gst_element_get_state (pipeline, &state, NULL, NULL); + if (state != GST_STATE_READY) { gst_element_set_state (pipeline, GST_STATE_READY); gtk_timeout_remove (update_id); } @@ -249,9 +240,7 @@ main (int argc, char **argv) pipeline = make_cdaudio_pipeline (); g_signal_connect (pipeline, "deep_notify", - G_CALLBACK (gst_element_default_deep_notify), NULL); - g_signal_connect (pipeline, "error", G_CALLBACK (gst_element_default_error), - NULL); + G_CALLBACK (gst_object_default_deep_notify), NULL); /* initialize gui elements ... */ window = gtk_window_new (GTK_WINDOW_TOPLEVEL); diff --git a/examples/seeking/playbin.c b/examples/seeking/playbin.c deleted file mode 100644 index b6ff35b8e2..0000000000 --- a/examples/seeking/playbin.c +++ /dev/null @@ -1,270 +0,0 @@ -#include -#include -#include -#include -#include - -static GstElement *playbin = NULL; -static GstElement *pipeline; -static guint64 duration; -static GtkAdjustment *adjustment; -static GtkWidget *hscale; -static gboolean verbose = FALSE; - -static guint update_id; - -#define UPDATE_INTERVAL 500 - -static GstElement * -make_playerbin_pipeline (const gchar * location) -{ - playbin = gst_element_factory_make ("playbin", "player"); - g_assert (playbin); - - g_object_set (G_OBJECT (playbin), "uri", location, NULL); - - return playbin; -} - -static gchar * -format_value (GtkScale * scale, gdouble value) -{ - gint64 real; - gint64 seconds; - gint64 subseconds; - - real = value * duration / 100; - seconds = (gint64) real / GST_SECOND; - subseconds = (gint64) real / (GST_SECOND / 100); - - return g_strdup_printf ("%02" G_GINT64_FORMAT ":%02" G_GINT64_FORMAT ":%02" - G_GINT64_FORMAT, seconds / 60, seconds % 60, subseconds % 100); -} - -static gboolean -update_scale (gpointer data) -{ - GstClock *clock; - guint64 position; - GstFormat format = GST_FORMAT_TIME; - gboolean res; - - duration = 0; - clock = gst_bin_get_clock (GST_BIN (pipeline)); - - res = gst_element_query (playbin, GST_QUERY_TOTAL, &format, &duration); - if (!res) - duration = 0; - res = gst_element_query (playbin, GST_QUERY_POSITION, &format, &position); - if (!res) - position = 0; - - if (position >= duration) - duration = position; - - if (duration > 0) { - gtk_adjustment_set_value (adjustment, position * 100.0 / duration); - gtk_widget_queue_draw (hscale); - } - - return TRUE; -} - -static gboolean -iterate (gpointer data) -{ - gboolean res; - - if (!GST_FLAG_IS_SET (GST_OBJECT (data), GST_BIN_SELF_SCHEDULABLE)) { - res = gst_bin_iterate (GST_BIN (data)); - } else { - g_usleep (UPDATE_INTERVAL); - res = gst_element_get_state (GST_ELEMENT (data)) == GST_STATE_PLAYING; - } - - if (!res) { - gtk_timeout_remove (update_id); - g_print ("stopping iterations\n"); - } - return res; -} - -static gboolean -start_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data) -{ - gst_element_set_state (pipeline, GST_STATE_PAUSED); - gtk_timeout_remove (update_id); - - return FALSE; -} - -static gboolean -stop_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data) -{ - gint64 real = gtk_range_get_value (GTK_RANGE (widget)) * duration / 100; - gboolean res; - GstEvent *s_event; - - g_print ("seek to %" G_GINT64_FORMAT " on element %s\n", real, - gst_element_get_name (playbin)); - s_event = - gst_event_new_seek (GST_FORMAT_TIME | GST_SEEK_METHOD_SET | - GST_SEEK_FLAG_FLUSH, real); - - res = gst_element_send_event (playbin, s_event); - - gst_element_set_state (pipeline, GST_STATE_PLAYING); - gtk_idle_add ((GtkFunction) iterate, pipeline); - update_id = - gtk_timeout_add (UPDATE_INTERVAL, (GtkFunction) update_scale, pipeline); - - return FALSE; -} - -static void -print_media_info (GstElement * playbin) -{ - GList *streaminfo; - GList *s; - - g_print ("have media info now\n"); - - /* get info about the stream */ - g_object_get (G_OBJECT (playbin), "stream-info", &streaminfo, NULL); - - for (s = streaminfo; s; s = g_list_next (s)) { - GObject *obj = G_OBJECT (s->data); - gint type; - gboolean mute; - - g_object_get (obj, "type", &type, NULL); - g_object_get (obj, "mute", &mute, NULL); - - g_print ("%d %d\n", type, mute); - } -} - -static void -play_cb (GtkButton * button, gpointer data) -{ - if (gst_element_get_state (pipeline) != GST_STATE_PLAYING) { - GstElementStateReturn res; - - res = gst_element_set_state (pipeline, GST_STATE_PAUSED); - if (res == GST_STATE_SUCCESS) { - print_media_info (playbin); - - res = gst_element_set_state (pipeline, GST_STATE_PLAYING); - gtk_idle_add ((GtkFunction) iterate, pipeline); - update_id = - gtk_timeout_add (UPDATE_INTERVAL, (GtkFunction) update_scale, - pipeline); - } else { - g_print ("failed playing\n"); - } - } -} - -static void -pause_cb (GtkButton * button, gpointer data) -{ - if (gst_element_get_state (pipeline) != GST_STATE_PAUSED) { - gst_element_set_state (pipeline, GST_STATE_PAUSED); - gtk_timeout_remove (update_id); - } -} - -static void -stop_cb (GtkButton * button, gpointer data) -{ - if (gst_element_get_state (pipeline) != GST_STATE_READY) { - gst_element_set_state (pipeline, GST_STATE_READY); - gtk_adjustment_set_value (adjustment, 0.0); - gtk_timeout_remove (update_id); - } -} - -static void -print_usage (int argc, char **argv) -{ - g_print ("usage: %s \n", argv[0]); -} - -int -main (int argc, char **argv) -{ - GtkWidget *window, *hbox, *vbox, *play_button, *pause_button, *stop_button; - struct poptOption options[] = { - {"verbose", 'v', POPT_ARG_NONE | POPT_ARGFLAG_STRIP, &verbose, 0, - "Verbose properties", NULL}, - POPT_TABLEEND - }; - - gst_init_with_popt_table (&argc, &argv, options); - gtk_init (&argc, &argv); - - if (argc != 2) { - print_usage (argc, argv); - exit (-1); - } - - pipeline = make_playerbin_pipeline (argv[1]); - g_assert (pipeline); - - /* initialize gui elements ... */ - window = gtk_window_new (GTK_WINDOW_TOPLEVEL); - hbox = gtk_hbox_new (FALSE, 0); - vbox = gtk_vbox_new (FALSE, 0); - play_button = gtk_button_new_with_label ("play"); - pause_button = gtk_button_new_with_label ("pause"); - stop_button = gtk_button_new_with_label ("stop"); - - adjustment = - GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.00, 100.0, 0.1, 1.0, 1.0)); - hscale = gtk_hscale_new (adjustment); - gtk_scale_set_digits (GTK_SCALE (hscale), 2); - gtk_range_set_update_policy (GTK_RANGE (hscale), GTK_UPDATE_CONTINUOUS); - - gtk_signal_connect (GTK_OBJECT (hscale), - "button_press_event", G_CALLBACK (start_seek), pipeline); - gtk_signal_connect (GTK_OBJECT (hscale), - "button_release_event", G_CALLBACK (stop_seek), pipeline); - gtk_signal_connect (GTK_OBJECT (hscale), - "format_value", G_CALLBACK (format_value), pipeline); - - /* do the packing stuff ... */ - gtk_window_set_default_size (GTK_WINDOW (window), 96, 96); - gtk_container_add (GTK_CONTAINER (window), vbox); - gtk_container_add (GTK_CONTAINER (vbox), hbox); - gtk_box_pack_start (GTK_BOX (hbox), play_button, FALSE, FALSE, 2); - gtk_box_pack_start (GTK_BOX (hbox), pause_button, FALSE, FALSE, 2); - gtk_box_pack_start (GTK_BOX (hbox), stop_button, FALSE, FALSE, 2); - gtk_box_pack_start (GTK_BOX (vbox), hscale, TRUE, TRUE, 2); - - /* connect things ... */ - g_signal_connect (G_OBJECT (play_button), "clicked", G_CALLBACK (play_cb), - pipeline); - g_signal_connect (G_OBJECT (pause_button), "clicked", G_CALLBACK (pause_cb), - pipeline); - g_signal_connect (G_OBJECT (stop_button), "clicked", G_CALLBACK (stop_cb), - pipeline); - g_signal_connect (G_OBJECT (window), "delete_event", gtk_main_quit, NULL); - - /* show the gui. */ - gtk_widget_show_all (window); - - if (verbose) { - g_signal_connect (pipeline, "deep_notify", - G_CALLBACK (gst_element_default_deep_notify), NULL); - } - g_signal_connect (pipeline, "error", G_CALLBACK (gst_element_default_error), - NULL); - - gtk_main (); - - gst_element_set_state (pipeline, GST_STATE_NULL); - - gst_object_unref (GST_OBJECT (pipeline)); - - return 0; -} diff --git a/examples/seeking/seek.c b/examples/seeking/seek.c index fdd7c45d18..ef73db6b88 100644 --- a/examples/seeking/seek.c +++ b/examples/seeking/seek.c @@ -1,6 +1,3 @@ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif #include #include #include @@ -20,12 +17,18 @@ static gboolean elem_seek = FALSE; static gboolean verbose = FALSE; static guint update_id; +static guint seek_timeout_id = 0; +static gulong changed_id; //#define SOURCE "gnomevfssrc" -#define SOURCE "filesrc" +#define SOURCE "gnomevfssrc" #define UPDATE_INTERVAL 500 +/* number of milliseconds to play for after a seek */ +#define SCRUB_TIME 250 +#define SCRUB + #define THREAD #define PAD_SEEK @@ -54,13 +57,13 @@ dynamic_link (GstPadTemplate * templ, GstPad * newpad, gpointer data) { dyn_link *connect = (dyn_link *) data; - if (!strcmp (gst_pad_get_name (newpad), connect->padname)) { - gst_element_set_state (pipeline, GST_STATE_PAUSED); - gst_bin_add (GST_BIN (pipeline), connect->bin); + if (connect->padname == NULL || + !strcmp (gst_pad_get_name (newpad), connect->padname)) { + if (connect->bin) + gst_bin_add (GST_BIN (pipeline), connect->bin); gst_pad_link (newpad, connect->target); - gst_element_set_state (pipeline, GST_STATE_PLAYING); - seekable_pads = g_list_prepend (seekable_pads, newpad); + //seekable_pads = g_list_prepend (seekable_pads, newpad); rate_pads = g_list_prepend (rate_pads, newpad); } } @@ -91,7 +94,7 @@ make_mod_pipeline (const gchar * location) src = gst_element_factory_make_or_warn (SOURCE, "src"); decoder = gst_element_factory_make_or_warn ("modplug", "decoder"); - audiosink = gst_element_factory_make_or_warn (DEFAULT_AUDIOSINK, "sink"); + audiosink = gst_element_factory_make_or_warn ("osssink", "sink"); //g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL); g_object_set (G_OBJECT (src), "location", location, NULL); @@ -122,8 +125,8 @@ make_dv_pipeline (const gchar * location) src = gst_element_factory_make_or_warn (SOURCE, "src"); decoder = gst_element_factory_make_or_warn ("dvdec", "decoder"); - videosink = gst_element_factory_make_or_warn (DEFAULT_VIDEOSINK, "v_sink"); - audiosink = gst_element_factory_make_or_warn (DEFAULT_AUDIOSINK, "a_sink"); + videosink = gst_element_factory_make_or_warn ("ximagesink", "v_sink"); + audiosink = gst_element_factory_make_or_warn ("osssink", "a_sink"); //g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL); g_object_set (G_OBJECT (src), "location", location, NULL); @@ -159,7 +162,7 @@ make_wav_pipeline (const gchar * location) src = gst_element_factory_make_or_warn (SOURCE, "src"); decoder = gst_element_factory_make_or_warn ("wavparse", "decoder"); - audiosink = gst_element_factory_make_or_warn (DEFAULT_AUDIOSINK, "sink"); + audiosink = gst_element_factory_make_or_warn ("osssink", "sink"); //g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL); g_object_set (G_OBJECT (src), "location", location, NULL); @@ -190,7 +193,7 @@ make_flac_pipeline (const gchar * location) src = gst_element_factory_make_or_warn (SOURCE, "src"); decoder = gst_element_factory_make_or_warn ("flacdec", "decoder"); - audiosink = gst_element_factory_make_or_warn (DEFAULT_AUDIOSINK, "sink"); + audiosink = gst_element_factory_make_or_warn ("osssink", "sink"); g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL); g_object_set (G_OBJECT (src), "location", location, NULL); @@ -221,7 +224,7 @@ make_sid_pipeline (const gchar * location) src = gst_element_factory_make_or_warn (SOURCE, "src"); decoder = gst_element_factory_make_or_warn ("siddec", "decoder"); - audiosink = gst_element_factory_make_or_warn (DEFAULT_AUDIOSINK, "sink"); + audiosink = gst_element_factory_make_or_warn ("osssink", "sink"); //g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL); g_object_set (G_OBJECT (src), "location", location, NULL); @@ -276,25 +279,35 @@ make_parse_pipeline (const gchar * location) static GstElement * make_vorbis_pipeline (const gchar * location) { - GstElement *pipeline; - GstElement *src, *decoder, *audiosink; + GstElement *pipeline, *audio_bin; + GstElement *src, *demux, *decoder, *convert, *audiosink; GstPad *seekable; pipeline = gst_pipeline_new ("app"); src = gst_element_factory_make_or_warn (SOURCE, "src"); - decoder = gst_element_factory_make_or_warn ("vorbisfile", "decoder"); - audiosink = gst_element_factory_make_or_warn (DEFAULT_AUDIOSINK, "sink"); + demux = gst_element_factory_make_or_warn ("oggdemux", "demux"); + decoder = gst_element_factory_make_or_warn ("vorbisdec", "decoder"); + convert = gst_element_factory_make_or_warn ("audioconvert", "convert"); + audiosink = gst_element_factory_make_or_warn ("osssink", "sink"); g_object_set (G_OBJECT (audiosink), "sync", TRUE, NULL); g_object_set (G_OBJECT (src), "location", location, NULL); - gst_bin_add (GST_BIN (pipeline), src); - gst_bin_add (GST_BIN (pipeline), decoder); - gst_bin_add (GST_BIN (pipeline), audiosink); + audio_bin = gst_bin_new ("a_decoder_bin"); - gst_element_link (src, decoder); - gst_element_link (decoder, audiosink); + gst_bin_add (GST_BIN (pipeline), src); + gst_bin_add (GST_BIN (pipeline), demux); + gst_bin_add (GST_BIN (audio_bin), decoder); + gst_bin_add (GST_BIN (audio_bin), convert); + gst_bin_add (GST_BIN (audio_bin), audiosink); + gst_bin_add (GST_BIN (pipeline), audio_bin); + + gst_element_link (src, demux); + gst_element_link (decoder, convert); + gst_element_link (convert, audiosink); + + setup_dynamic_link (demux, NULL, gst_element_get_pad (decoder, "sink"), NULL); seekable = gst_element_get_pad (decoder, "src"); seekable_pads = g_list_prepend (seekable_pads, seekable); @@ -304,11 +317,185 @@ make_vorbis_pipeline (const gchar * location) return pipeline; } +static GstElement * +make_theora_pipeline (const gchar * location) +{ + GstElement *pipeline, *video_bin; + GstElement *src, *demux, *decoder, *convert, *videosink; + GstElement *queue; + GstPad *seekable; + + pipeline = gst_pipeline_new ("app"); + + src = gst_element_factory_make_or_warn (SOURCE, "src"); + demux = gst_element_factory_make_or_warn ("oggdemux", "demux"); + queue = gst_element_factory_make_or_warn ("queue", "queue"); + decoder = gst_element_factory_make_or_warn ("theoradec", "decoder"); + convert = gst_element_factory_make_or_warn ("ffmpegcolorspace", "convert"); + videosink = gst_element_factory_make_or_warn ("xvimagesink", "sink"); + + g_object_set (G_OBJECT (src), "location", location, NULL); + + video_bin = gst_bin_new ("v_decoder_bin"); + + gst_bin_add (GST_BIN (pipeline), src); + gst_bin_add (GST_BIN (pipeline), demux); + gst_bin_add (GST_BIN (pipeline), queue); + gst_bin_add (GST_BIN (video_bin), decoder); + gst_bin_add (GST_BIN (video_bin), convert); + gst_bin_add (GST_BIN (video_bin), videosink); + gst_bin_add (GST_BIN (pipeline), video_bin); + + gst_element_link (src, demux); + gst_element_link (queue, decoder); + gst_element_link (decoder, convert); + gst_element_link (convert, videosink); + + setup_dynamic_link (demux, NULL, gst_element_get_pad (queue, "sink"), NULL); + + seekable = gst_element_get_pad (decoder, "src"); + seekable_pads = g_list_prepend (seekable_pads, seekable); + rate_pads = g_list_prepend (rate_pads, seekable); + rate_pads = g_list_prepend (rate_pads, gst_element_get_pad (decoder, "sink")); + + return pipeline; +} + +static GstElement * +make_vorbis_theora_pipeline (const gchar * location) +{ + GstElement *pipeline, *audio_bin, *video_bin; + GstElement *src, *demux, *a_decoder, *a_convert, *v_decoder, *v_convert; + GstElement *audiosink, *videosink; + GstElement *a_queue, *v_queue; + GstPad *seekable; + + pipeline = gst_pipeline_new ("app"); + + src = gst_element_factory_make_or_warn (SOURCE, "src"); + g_object_set (G_OBJECT (src), "location", location, NULL); + + demux = gst_element_factory_make_or_warn ("oggdemux", "demux"); + + gst_bin_add (GST_BIN (pipeline), src); + gst_bin_add (GST_BIN (pipeline), demux); + gst_element_link (src, demux); + + audio_bin = gst_bin_new ("a_decoder_bin"); + a_queue = gst_element_factory_make_or_warn ("queue", "a_queue"); + a_decoder = gst_element_factory_make_or_warn ("vorbisdec", "a_dec"); + a_convert = gst_element_factory_make_or_warn ("audioconvert", "a_convert"); + audiosink = gst_element_factory_make_or_warn ("osssink", "a_sink"); + + gst_element_link (a_queue, a_decoder); + gst_element_link (a_decoder, a_convert); + gst_element_link (a_convert, audiosink); + + gst_bin_add (GST_BIN (audio_bin), a_queue); + gst_bin_add (GST_BIN (audio_bin), a_decoder); + gst_bin_add (GST_BIN (audio_bin), a_convert); + gst_bin_add (GST_BIN (audio_bin), audiosink); + + gst_bin_add (GST_BIN (pipeline), audio_bin); + + setup_dynamic_link (demux, NULL, gst_element_get_pad (a_queue, "sink"), NULL); + + video_bin = gst_bin_new ("v_decoder_bin"); + v_queue = gst_element_factory_make_or_warn ("queue", "v_queue"); + v_decoder = gst_element_factory_make_or_warn ("theoradec", "v_dec"); + v_convert = + gst_element_factory_make_or_warn ("ffmpegcolorspace", "v_convert"); + videosink = gst_element_factory_make_or_warn ("xvimagesink", "v_sink"); + gst_element_link_many (v_queue, v_decoder, v_convert, videosink, NULL); + + gst_bin_add (GST_BIN (video_bin), v_queue); + gst_bin_add (GST_BIN (video_bin), v_decoder); + gst_bin_add (GST_BIN (video_bin), v_convert); + gst_bin_add (GST_BIN (video_bin), videosink); + + gst_bin_add (GST_BIN (pipeline), video_bin); + + setup_dynamic_link (demux, NULL, gst_element_get_pad (v_queue, "sink"), NULL); + + seekable = gst_element_get_pad (a_decoder, "src"); + seekable_pads = g_list_prepend (seekable_pads, seekable); + rate_pads = g_list_prepend (rate_pads, seekable); + rate_pads = + g_list_prepend (rate_pads, gst_element_get_pad (a_decoder, "sink")); + + return pipeline; +} + +static GstElement * +make_avi_msmpeg4v3_mp3_pipeline (const gchar * location) +{ + GstElement *pipeline, *audio_bin, *video_bin; + GstElement *src, *demux, *a_decoder, *a_convert, *v_decoder, *v_convert; + GstElement *audiosink, *videosink; + GstElement *a_queue, *v_queue; + GstPad *seekable; + + pipeline = gst_pipeline_new ("app"); + + src = gst_element_factory_make_or_warn (SOURCE, "src"); + g_object_set (G_OBJECT (src), "location", location, NULL); + + demux = gst_element_factory_make_or_warn ("avidemux", "demux"); + + gst_bin_add (GST_BIN (pipeline), src); + gst_bin_add (GST_BIN (pipeline), demux); + gst_element_link (src, demux); + + audio_bin = gst_bin_new ("a_decoder_bin"); + a_queue = gst_element_factory_make_or_warn ("queue", "a_queue"); + a_decoder = gst_element_factory_make_or_warn ("mad", "a_dec"); + a_convert = gst_element_factory_make_or_warn ("audioconvert", "a_convert"); + audiosink = gst_element_factory_make_or_warn ("osssink", "a_sink"); + + gst_element_link (a_queue, a_decoder); + gst_element_link (a_decoder, a_convert); + gst_element_link (a_convert, audiosink); + + gst_bin_add (GST_BIN (audio_bin), a_queue); + gst_bin_add (GST_BIN (audio_bin), a_decoder); + gst_bin_add (GST_BIN (audio_bin), a_convert); + gst_bin_add (GST_BIN (audio_bin), audiosink); + + gst_bin_add (GST_BIN (pipeline), audio_bin); + + setup_dynamic_link (demux, NULL, gst_element_get_pad (a_queue, "sink"), NULL); + + video_bin = gst_bin_new ("v_decoder_bin"); + v_queue = gst_element_factory_make_or_warn ("queue", "v_queue"); + v_decoder = gst_element_factory_make_or_warn ("ffdec_msmpeg4", "v_dec"); + v_convert = + gst_element_factory_make_or_warn ("ffmpegcolorspace", "v_convert"); + videosink = gst_element_factory_make_or_warn ("xvimagesink", "v_sink"); + gst_element_link_many (v_queue, v_decoder, v_convert, videosink, NULL); + + gst_bin_add (GST_BIN (video_bin), v_queue); + gst_bin_add (GST_BIN (video_bin), v_decoder); + gst_bin_add (GST_BIN (video_bin), v_convert); + gst_bin_add (GST_BIN (video_bin), videosink); + + gst_bin_add (GST_BIN (pipeline), video_bin); + + setup_dynamic_link (demux, NULL, gst_element_get_pad (v_queue, "sink"), NULL); + + seekable = gst_element_get_pad (a_decoder, "src"); + seekable_pads = g_list_prepend (seekable_pads, seekable); + rate_pads = g_list_prepend (rate_pads, seekable); + rate_pads = + g_list_prepend (rate_pads, gst_element_get_pad (a_decoder, "sink")); + + return pipeline; +} + static GstElement * make_mp3_pipeline (const gchar * location) { GstElement *pipeline; - GstElement *src, *decoder, *audiosink, *queue, *audio_thread; + GstElement *src, *decoder, *osssink, *queue; GstPad *seekable; pipeline = gst_pipeline_new ("app"); @@ -316,24 +503,21 @@ make_mp3_pipeline (const gchar * location) src = gst_element_factory_make_or_warn (SOURCE, "src"); decoder = gst_element_factory_make_or_warn ("mad", "dec"); queue = gst_element_factory_make_or_warn ("queue", "queue"); - audiosink = gst_element_factory_make_or_warn (DEFAULT_AUDIOSINK, "sink"); + osssink = gst_element_factory_make_or_warn ("osssink", "sink"); - audio_thread = gst_thread_new ("a_decoder_thread"); - - seekable_elements = g_list_prepend (seekable_elements, audiosink); + seekable_elements = g_list_prepend (seekable_elements, osssink); g_object_set (G_OBJECT (src), "location", location, NULL); - g_object_set (G_OBJECT (audiosink), "fragment", 0x00180008, NULL); + g_object_set (G_OBJECT (osssink), "fragment", 0x00180008, NULL); gst_bin_add (GST_BIN (pipeline), src); gst_bin_add (GST_BIN (pipeline), decoder); - gst_bin_add (GST_BIN (audio_thread), queue); - gst_bin_add (GST_BIN (audio_thread), audiosink); - gst_bin_add (GST_BIN (pipeline), audio_thread); + gst_bin_add (GST_BIN (pipeline), queue); + gst_bin_add (GST_BIN (pipeline), osssink); gst_element_link (src, decoder); gst_element_link (decoder, queue); - gst_element_link (queue, audiosink); + gst_element_link (queue, osssink); seekable = gst_element_get_pad (queue, "src"); seekable_pads = g_list_prepend (seekable_pads, seekable); @@ -348,8 +532,7 @@ make_avi_pipeline (const gchar * location) { GstElement *pipeline, *audio_bin, *video_bin; GstElement *src, *demux, *a_decoder, *v_decoder, *audiosink, *videosink; - GstElement *a_queue = NULL, *audio_thread = NULL, *v_queue = - NULL, *video_thread = NULL; + GstElement *a_queue = NULL, *v_queue = NULL; GstPad *seekable; pipeline = gst_pipeline_new ("app"); @@ -366,16 +549,14 @@ make_avi_pipeline (const gchar * location) audio_bin = gst_bin_new ("a_decoder_bin"); a_decoder = gst_element_factory_make_or_warn ("mad", "a_dec"); - audio_thread = gst_thread_new ("a_decoder_thread"); - audiosink = gst_element_factory_make_or_warn (DEFAULT_AUDIOSINK, "a_sink"); + audiosink = gst_element_factory_make_or_warn ("osssink", "a_sink"); //g_object_set (G_OBJECT (audiosink), "fragment", 0x00180008, NULL); a_queue = gst_element_factory_make_or_warn ("queue", "a_queue"); gst_element_link (a_decoder, a_queue); gst_element_link (a_queue, audiosink); gst_bin_add (GST_BIN (audio_bin), a_decoder); - gst_bin_add (GST_BIN (audio_bin), audio_thread); - gst_bin_add (GST_BIN (audio_thread), a_queue); - gst_bin_add (GST_BIN (audio_thread), audiosink); + gst_bin_add (GST_BIN (audio_bin), a_queue); + gst_bin_add (GST_BIN (audio_bin), audiosink); gst_element_set_state (audio_bin, GST_STATE_PAUSED); setup_dynamic_link (demux, "audio_00", gst_element_get_pad (a_decoder, @@ -391,8 +572,7 @@ make_avi_pipeline (const gchar * location) //v_decoder = gst_element_factory_make_or_warn ("identity", "v_dec"); //v_decoder = gst_element_factory_make_or_warn ("windec", "v_dec"); v_decoder = gst_element_factory_make_or_warn ("ffmpegdecall", "v_dec"); - video_thread = gst_thread_new ("v_decoder_thread"); - videosink = gst_element_factory_make_or_warn (DEFAULT_VIDEOSINK, "v_sink"); + videosink = gst_element_factory_make_or_warn ("ximagesink", "v_sink"); //videosink = gst_element_factory_make_or_warn ("fakesink", "v_sink"); //g_object_set (G_OBJECT (videosink), "sync", TRUE, NULL); v_queue = gst_element_factory_make_or_warn ("queue", "v_queue"); @@ -400,9 +580,8 @@ make_avi_pipeline (const gchar * location) gst_element_link (v_decoder, v_queue); gst_element_link (v_queue, videosink); gst_bin_add (GST_BIN (video_bin), v_decoder); - gst_bin_add (GST_BIN (video_bin), video_thread); - gst_bin_add (GST_BIN (video_thread), v_queue); - gst_bin_add (GST_BIN (video_thread), videosink); + gst_bin_add (GST_BIN (video_bin), v_queue); + gst_bin_add (GST_BIN (video_bin), videosink); gst_element_set_state (video_bin, GST_STATE_PAUSED); @@ -424,7 +603,7 @@ make_mpeg_pipeline (const gchar * location) GstElement *pipeline, *audio_bin, *video_bin; GstElement *src, *demux, *a_decoder, *v_decoder, *v_filter; GstElement *audiosink, *videosink; - GstElement *a_queue, *audio_thread, *v_queue, *video_thread; + GstElement *a_queue, *v_queue; GstPad *seekable; pipeline = gst_pipeline_new ("app"); @@ -443,16 +622,14 @@ make_mpeg_pipeline (const gchar * location) audio_bin = gst_bin_new ("a_decoder_bin"); a_decoder = gst_element_factory_make_or_warn ("mad", "a_dec"); - audio_thread = gst_thread_new ("a_decoder_thread"); a_queue = gst_element_factory_make_or_warn ("queue", "a_queue"); - audiosink = gst_element_factory_make_or_warn (DEFAULT_AUDIOSINK, "a_sink"); + audiosink = gst_element_factory_make_or_warn ("osssink", "a_sink"); g_object_set (G_OBJECT (audiosink), "fragment", 0x00180008, NULL); gst_element_link (a_decoder, a_queue); gst_element_link (a_queue, audiosink); gst_bin_add (GST_BIN (audio_bin), a_decoder); - gst_bin_add (GST_BIN (audio_bin), audio_thread); - gst_bin_add (GST_BIN (audio_thread), a_queue); - gst_bin_add (GST_BIN (audio_thread), audiosink); + gst_bin_add (GST_BIN (audio_bin), a_queue); + gst_bin_add (GST_BIN (audio_bin), audiosink); setup_dynamic_link (demux, "audio_00", gst_element_get_pad (a_decoder, "sink"), audio_bin); @@ -465,16 +642,15 @@ make_mpeg_pipeline (const gchar * location) video_bin = gst_bin_new ("v_decoder_bin"); v_decoder = gst_element_factory_make_or_warn ("mpeg2dec", "v_dec"); - video_thread = gst_thread_new ("v_decoder_thread"); //g_object_set (G_OBJECT (video_thread), "priority", 2, NULL); v_queue = gst_element_factory_make_or_warn ("queue", "v_queue"); v_filter = gst_element_factory_make_or_warn ("ffmpegcolorspace", "v_filter"); - videosink = gst_element_factory_make_or_warn (DEFAULT_VIDEOSINK, "v_sink"); + videosink = gst_element_factory_make_or_warn ("ximagesink", "v_sink"); gst_element_link_many (v_decoder, v_queue, v_filter, NULL); gst_element_link (v_filter, videosink); - gst_bin_add_many (GST_BIN (video_bin), v_decoder, video_thread, NULL); - gst_bin_add_many (GST_BIN (video_thread), v_queue, v_filter, videosink, NULL); + gst_bin_add_many (GST_BIN (video_bin), v_decoder, NULL); + gst_bin_add_many (GST_BIN (video_bin), v_queue, v_filter, videosink, NULL); setup_dynamic_link (demux, "video_00", gst_element_get_pad (v_decoder, "sink"), video_bin); @@ -494,7 +670,7 @@ make_mpegnt_pipeline (const gchar * location) GstElement *pipeline, *audio_bin, *video_bin; GstElement *src, *demux, *a_decoder, *v_decoder, *v_filter; GstElement *audiosink, *videosink; - GstElement *a_queue, *audio_thread; + GstElement *a_queue; GstPad *seekable; pipeline = gst_pipeline_new ("app"); @@ -513,17 +689,15 @@ make_mpegnt_pipeline (const gchar * location) audio_bin = gst_bin_new ("a_decoder_bin"); a_decoder = gst_element_factory_make_or_warn ("mad", "a_dec"); - audio_thread = gst_thread_new ("a_decoder_thread"); a_queue = gst_element_factory_make_or_warn ("queue", "a_queue"); - audiosink = gst_element_factory_make_or_warn (DEFAULT_AUDIOSINK, "a_sink"); + audiosink = gst_element_factory_make_or_warn ("osssink", "a_sink"); //g_object_set (G_OBJECT (audiosink), "fragment", 0x00180008, NULL); g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL); gst_element_link (a_decoder, a_queue); gst_element_link (a_queue, audiosink); gst_bin_add (GST_BIN (audio_bin), a_decoder); - gst_bin_add (GST_BIN (audio_bin), audio_thread); - gst_bin_add (GST_BIN (audio_thread), a_queue); - gst_bin_add (GST_BIN (audio_thread), audiosink); + gst_bin_add (GST_BIN (audio_bin), a_queue); + gst_bin_add (GST_BIN (audio_bin), audiosink); setup_dynamic_link (demux, "audio_00", gst_element_get_pad (a_decoder, "sink"), audio_bin); @@ -537,7 +711,7 @@ make_mpegnt_pipeline (const gchar * location) video_bin = gst_bin_new ("v_decoder_bin"); v_decoder = gst_element_factory_make_or_warn ("mpeg2dec", "v_dec"); v_filter = gst_element_factory_make_or_warn ("ffmpegcolorspace", "v_filter"); - videosink = gst_element_factory_make_or_warn (DEFAULT_VIDEOSINK, "v_sink"); + videosink = gst_element_factory_make_or_warn ("ximagesink", "v_sink"); gst_element_link_many (v_decoder, v_filter, videosink, NULL); gst_bin_add_many (GST_BIN (video_bin), v_decoder, v_filter, videosink, NULL); @@ -554,45 +728,15 @@ make_mpegnt_pipeline (const gchar * location) return pipeline; } -static GstCaps * -fixate (GstPad * pad, const GstCaps * in_caps, gpointer data) -{ - GstCaps *caps; - GstStructure *s; - - if (gst_caps_get_size (in_caps) > 1) - return NULL; - - /* nothing if fixed already */ - s = gst_caps_get_structure (in_caps, 0); - if (gst_structure_has_field_typed (s, "width", G_TYPE_INT) && - gst_structure_has_field_typed (s, "height", G_TYPE_INT) && - gst_structure_has_field_typed (s, "framerate", G_TYPE_DOUBLE)) - return NULL; - - /* fixate */ - caps = gst_caps_copy (in_caps); - s = gst_caps_get_structure (caps, 0); - gst_caps_structure_fixate_field_nearest_int (s, "width", 200); - gst_caps_structure_fixate_field_nearest_int (s, "height", 150); - gst_caps_structure_fixate_field_nearest_double (s, "framerate", 10.0); - - return caps; -} - static GstElement * make_playerbin_pipeline (const gchar * location) { - GstElement *player, *vis; + GstElement *player; player = gst_element_factory_make ("playbin", "player"); - vis = gst_element_factory_make ("synaesthesia", "vis"); g_assert (player); - g_assert (vis); - g_signal_connect (gst_element_get_pad (vis, "src"), "fixate", - G_CALLBACK (fixate), NULL); - g_object_set (G_OBJECT (player), "uri", location, "vis-plugin", vis, NULL); + g_object_set (G_OBJECT (player), "uri", location, NULL); seekable_elements = g_list_prepend (seekable_elements, player); @@ -791,7 +935,7 @@ update_scale (gpointer data) gboolean res; duration = 0; - clock = gst_bin_get_clock (GST_BIN (pipeline)); + clock = gst_pipeline_get_clock (GST_PIPELINE (pipeline)); if (elem_seek) { if (seekable_elements) { @@ -843,36 +987,21 @@ update_scale (gpointer data) return TRUE; } +static void do_seek (GtkWidget * widget); + +#ifdef SCRUB static gboolean -iterate (gpointer data) -{ - gboolean res; - - if (!GST_FLAG_IS_SET (GST_OBJECT (data), GST_BIN_SELF_SCHEDULABLE)) { - res = gst_bin_iterate (GST_BIN (data)); - } else { - g_usleep (500); - res = gst_element_get_state (GST_ELEMENT (data)) == GST_STATE_PLAYING; - } - - if (!res) { - gtk_timeout_remove (update_id); - g_print ("stopping iterations\n"); - } - return res; -} - -static gboolean -start_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data) +end_scrub (GtkWidget * widget) { gst_element_set_state (pipeline, GST_STATE_PAUSED); - gtk_timeout_remove (update_id); + seek_timeout_id = 0; return FALSE; } +#endif -static gboolean -stop_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data) +static void +do_seek (GtkWidget * widget) { gint64 real = gtk_range_get_value (GTK_RANGE (widget)) * duration / 100; gboolean res; @@ -884,8 +1013,8 @@ stop_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data) while (walk) { GstPad *seekable = GST_PAD (walk->data); - g_print ("seek to %" G_GINT64_FORMAT " on pad %s:%s\n", real, - GST_DEBUG_PAD_NAME (seekable)); + g_print ("seek to %" GST_TIME_FORMAT " on pad %s:%s\n", + GST_TIME_ARGS (real), GST_DEBUG_PAD_NAME (seekable)); s_event = gst_event_new_seek (GST_FORMAT_TIME | GST_SEEK_METHOD_SET | GST_SEEK_FLAG_FLUSH, real); @@ -912,8 +1041,57 @@ stop_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data) } } + GST_PIPELINE (pipeline)->stream_time = real; +} + +static void +seek_cb (GtkWidget * widget) +{ +#ifdef SCRUB + /* If the timer hasn't expired yet, then the pipeline is running */ + if (seek_timeout_id != 0) { + gst_element_set_state (pipeline, GST_STATE_PAUSED); + } +#endif + + do_seek (widget); + +#ifdef SCRUB gst_element_set_state (pipeline, GST_STATE_PLAYING); - gtk_idle_add ((GtkFunction) iterate, pipeline); + + if (seek_timeout_id == 0) { + seek_timeout_id = + gtk_timeout_add (SCRUB_TIME, (GSourceFunc) end_scrub, widget); + } +#endif +} + +static gboolean +start_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data) +{ + gst_element_set_state (pipeline, GST_STATE_PAUSED); + gtk_timeout_remove (update_id); + + if (changed_id == 0) { + changed_id = gtk_signal_connect (GTK_OBJECT (hscale), + "value_changed", G_CALLBACK (seek_cb), pipeline); + } + + return FALSE; +} + +static gboolean +stop_seek (GtkWidget * widget, gpointer user_data) +{ + g_signal_handler_disconnect (GTK_OBJECT (hscale), changed_id); + changed_id = 0; + if (seek_timeout_id != 0) { + gtk_timeout_remove (seek_timeout_id); + seek_timeout_id = 0; + /* Still scrubbing, so the pipeline is already playing */ + } else + gst_element_set_state (pipeline, GST_STATE_PLAYING); + update_id = gtk_timeout_add (UPDATE_INTERVAL, (GtkFunction) update_scale, pipeline); @@ -923,9 +1101,11 @@ stop_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data) static void play_cb (GtkButton * button, gpointer data) { - if (gst_element_get_state (pipeline) != GST_STATE_PLAYING) { + GstElementState state; + + gst_element_get_state (pipeline, &state, NULL, NULL); + if (state != GST_STATE_PLAYING) { gst_element_set_state (pipeline, GST_STATE_PLAYING); - gtk_idle_add ((GtkFunction) iterate, pipeline); update_id = gtk_timeout_add (UPDATE_INTERVAL, (GtkFunction) update_scale, pipeline); } @@ -934,7 +1114,10 @@ play_cb (GtkButton * button, gpointer data) static void pause_cb (GtkButton * button, gpointer data) { - if (gst_element_get_state (pipeline) != GST_STATE_PAUSED) { + GstElementState state; + + gst_element_get_state (pipeline, &state, NULL, NULL); + if (state != GST_STATE_PAUSED) { gst_element_set_state (pipeline, GST_STATE_PAUSED); gtk_timeout_remove (update_id); } @@ -943,7 +1126,10 @@ pause_cb (GtkButton * button, gpointer data) static void stop_cb (GtkButton * button, gpointer data) { - if (gst_element_get_state (pipeline) != GST_STATE_READY) { + GstElementState state; + + gst_element_get_state (pipeline, &state, NULL, NULL); + if (state != GST_STATE_READY) { gst_element_set_state (pipeline, GST_STATE_READY); gtk_adjustment_set_value (adjustment, 0.0); gtk_timeout_remove (update_id); @@ -963,6 +1149,9 @@ static Pipeline pipelines[] = { {"mpeg1", make_mpeg_pipeline}, {"mpegparse", make_parse_pipeline}, {"vorbis", make_vorbis_pipeline}, + {"theora", make_theora_pipeline}, + {"ogg/v/t", make_vorbis_theora_pipeline}, + {"avi/msmpeg4v3/mp3", make_avi_msmpeg4v3_mp3_pipeline}, {"sid", make_sid_pipeline}, {"flac", make_flac_pipeline}, {"wav", make_wav_pipeline}, @@ -1065,18 +1254,13 @@ main (int argc, char **argv) if (verbose) { g_signal_connect (pipeline, "deep_notify", - G_CALLBACK (gst_element_default_deep_notify), NULL); + G_CALLBACK (gst_object_default_deep_notify), NULL); } - g_signal_connect (pipeline, "error", G_CALLBACK (gst_element_default_error), - NULL); - gtk_main (); gst_element_set_state (pipeline, GST_STATE_NULL); - //gst_object_unref (GST_OBJECT (pipeline)); - - //g_mem_chunk_info(); + gst_object_unref (GST_OBJECT (pipeline)); return 0; } diff --git a/examples/seeking/spider_seek.c b/examples/seeking/spider_seek.c deleted file mode 100644 index d63d3bde84..0000000000 --- a/examples/seeking/spider_seek.c +++ /dev/null @@ -1,379 +0,0 @@ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif -#include -#include -#include -#include -#include - -static GList *rate_pads = NULL; -static GList *seekable_elements = NULL; - -static GstElement *pipeline; -static GtkAdjustment *adjustment; -static gboolean stats = FALSE; -static guint64 duration; - -static guint update_id; - -//#define SOURCE "gnomevfssrc" -#define SOURCE "filesrc" - -#define UPDATE_INTERVAL 500 - -static GstElement * -make_spider_pipeline (const gchar * location, gboolean thread) -{ - GstElement *pipeline; - GstElement *src, *decoder, *audiosink, *videosink, *a_thread, *v_thread, - *a_queue, *v_queue; - - if (thread) { - pipeline = gst_thread_new ("app"); - } else { - pipeline = gst_pipeline_new ("app"); - } - - - src = gst_element_factory_make (SOURCE, "src"); - decoder = gst_element_factory_make ("spider", "decoder"); - a_thread = gst_thread_new ("a_thread"); - a_queue = gst_element_factory_make ("queue", "a_queue"); - audiosink = gst_element_factory_make (DEFAULT_AUDIOSINK, "a_sink"); - //g_object_set (G_OBJECT (audiosink), "fragment", 0x00180008, NULL); - - v_thread = gst_thread_new ("v_thread"); - v_queue = gst_element_factory_make ("queue", "v_queue"); - videosink = gst_element_factory_make (DEFAULT_VIDEOSINK, "v_sink"); - //g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL); - - g_object_set (G_OBJECT (src), "location", location, NULL); - - gst_bin_add (GST_BIN (pipeline), src); - gst_bin_add (GST_BIN (pipeline), decoder); - gst_bin_add (GST_BIN (a_thread), a_queue); - gst_bin_add (GST_BIN (a_thread), audiosink); - gst_bin_add (GST_BIN (v_thread), v_queue); - gst_bin_add (GST_BIN (v_thread), videosink); - gst_bin_add (GST_BIN (pipeline), a_thread); - gst_bin_add (GST_BIN (pipeline), v_thread); - - gst_element_link (src, decoder); - gst_element_link (v_queue, videosink); - gst_element_link (decoder, v_queue); - gst_element_link (a_queue, audiosink); - gst_element_link (decoder, a_queue); - - seekable_elements = g_list_prepend (seekable_elements, videosink); - seekable_elements = g_list_prepend (seekable_elements, audiosink); - rate_pads = - g_list_prepend (rate_pads, gst_element_get_pad (audiosink, "sink")); - rate_pads = - g_list_prepend (rate_pads, gst_element_get_pad (videosink, "sink")); - - return pipeline; -} - -static gchar * -format_value (GtkScale * scale, gdouble value) -{ - gint64 real; - gint64 seconds; - gint64 subseconds; - - real = value * duration / 100; - seconds = (gint64) real / GST_SECOND; - subseconds = (gint64) real / (GST_SECOND / 100); - - return g_strdup_printf ("%02" G_GINT64_FORMAT ":%02" G_GINT64_FORMAT ":%02" - G_GINT64_FORMAT, seconds / 60, seconds % 60, subseconds % 100); -} - -typedef struct -{ - const gchar *name; - const GstFormat format; -} -seek_format; - -static seek_format seek_formats[] = { - {"tim", GST_FORMAT_TIME}, - {"byt", GST_FORMAT_BYTES}, - {"buf", GST_FORMAT_BUFFERS}, - {"def", GST_FORMAT_DEFAULT}, - {NULL, 0}, -}; - -G_GNUC_UNUSED static void -query_rates (void) -{ - GList *walk = rate_pads; - - while (walk) { - GstPad *pad = GST_PAD (walk->data); - gint i = 0; - - g_print ("rate/sec %8.8s: ", GST_PAD_NAME (pad)); - while (seek_formats[i].name) { - gint64 value; - GstFormat format; - - format = seek_formats[i].format; - - if (gst_pad_convert (pad, GST_FORMAT_TIME, GST_SECOND, &format, &value)) { - g_print ("%s %13" G_GINT64_FORMAT " | ", seek_formats[i].name, value); - } else { - g_print ("%s %13.13s | ", seek_formats[i].name, "*NA*"); - } - - i++; - } - g_print (" %s:%s\n", GST_DEBUG_PAD_NAME (pad)); - - walk = g_list_next (walk); - } -} - -G_GNUC_UNUSED static void -query_durations () -{ - GList *walk = seekable_elements; - - while (walk) { - GstElement *element = GST_ELEMENT (walk->data); - gint i = 0; - - g_print ("durations %8.8s: ", GST_ELEMENT_NAME (element)); - while (seek_formats[i].name) { - gboolean res; - gint64 value; - GstFormat format; - - format = seek_formats[i].format; - res = gst_element_query (element, GST_QUERY_TOTAL, &format, &value); - if (res) { - g_print ("%s %13" G_GINT64_FORMAT " | ", seek_formats[i].name, value); - } else { - g_print ("%s %13.13s | ", seek_formats[i].name, "*NA*"); - } - i++; - } - g_print (" %s\n", GST_ELEMENT_NAME (element)); - walk = g_list_next (walk); - } -} - -G_GNUC_UNUSED static void -query_positions () -{ - GList *walk = seekable_elements; - - while (walk) { - GstElement *element = GST_ELEMENT (walk->data); - gint i = 0; - - g_print ("positions %8.8s: ", GST_ELEMENT_NAME (element)); - while (seek_formats[i].name) { - gboolean res; - gint64 value; - GstFormat format; - - format = seek_formats[i].format; - res = gst_element_query (element, GST_QUERY_POSITION, &format, &value); - if (res) { - g_print ("%s %13" G_GINT64_FORMAT " | ", seek_formats[i].name, value); - } else { - g_print ("%s %13.13s | ", seek_formats[i].name, "*NA*"); - } - i++; - } - g_print (" %s\n", GST_ELEMENT_NAME (element)); - walk = g_list_next (walk); - } -} - -static gboolean -update_scale (gpointer data) -{ - GstClock *clock; - guint64 position; - GstFormat format = GST_FORMAT_TIME; - - duration = 0; - clock = gst_bin_get_clock (GST_BIN (pipeline)); - - if (seekable_elements) { - GstElement *element = GST_ELEMENT (seekable_elements->data); - - gst_element_query (element, GST_QUERY_TOTAL, &format, &duration); - } - position = gst_clock_get_time (clock); - - if (stats) { - g_print ("clock: %13" G_GUINT64_FORMAT " (%s)\n", - position, gst_object_get_name (GST_OBJECT (clock))); - query_durations (); - query_positions (); - query_rates (); - } - if (duration > 0) { - gtk_adjustment_set_value (adjustment, position * 100.0 / duration); - } - - return TRUE; -} - -static gboolean -iterate (gpointer data) -{ - gboolean res = TRUE; - - res = gst_bin_iterate (GST_BIN (data)); - if (!res) { - gtk_timeout_remove (update_id); - g_print ("stopping iterations\n"); - } - return res; -} - -static gboolean -start_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data) -{ - gst_element_set_state (pipeline, GST_STATE_PAUSED); - gtk_timeout_remove (update_id); - - return FALSE; -} - -static gboolean -stop_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data) -{ - gint64 real = gtk_range_get_value (GTK_RANGE (widget)) * duration / 100; - gboolean res; - GstEvent *s_event; - GList *walk = seekable_elements; - - while (walk) { - GstElement *seekable = GST_ELEMENT (walk->data); - - g_print ("seek to %" G_GINT64_FORMAT " on element %s\n", real, - GST_ELEMENT_NAME (seekable)); - s_event = - gst_event_new_seek (GST_FORMAT_TIME | GST_SEEK_METHOD_SET | - GST_SEEK_FLAG_FLUSH, real); - - res = gst_element_send_event (seekable, s_event); - - walk = g_list_next (walk); - } - - gst_element_set_state (pipeline, GST_STATE_PLAYING); - if (!GST_FLAG_IS_SET (pipeline, GST_BIN_SELF_SCHEDULABLE)) - gtk_idle_add ((GtkFunction) iterate, pipeline); - update_id = - gtk_timeout_add (UPDATE_INTERVAL, (GtkFunction) update_scale, pipeline); - - return FALSE; -} - -static void -play_cb (GtkButton * button, gpointer data) -{ - if (gst_element_get_state (pipeline) != GST_STATE_PLAYING) { - gst_element_set_state (pipeline, GST_STATE_PLAYING); - if (!GST_FLAG_IS_SET (pipeline, GST_BIN_SELF_SCHEDULABLE)) - gtk_idle_add ((GtkFunction) iterate, pipeline); - update_id = - gtk_timeout_add (UPDATE_INTERVAL, (GtkFunction) update_scale, pipeline); - } -} - -static void -pause_cb (GtkButton * button, gpointer data) -{ - if (gst_element_get_state (pipeline) != GST_STATE_PAUSED) { - gst_element_set_state (pipeline, GST_STATE_PAUSED); - gtk_timeout_remove (update_id); - } -} - -static void -stop_cb (GtkButton * button, gpointer data) -{ - if (gst_element_get_state (pipeline) != GST_STATE_READY) { - gst_element_set_state (pipeline, GST_STATE_READY); - gtk_timeout_remove (update_id); - } -} - -int -main (int argc, char **argv) -{ - GtkWidget *window, *hbox, *vbox, - *play_button, *pause_button, *stop_button, *hscale; - gboolean threaded = FALSE; - struct poptOption options[] = { - {"threaded", 't', POPT_ARG_NONE | POPT_ARGFLAG_STRIP, &threaded, 0, - "Run the pipeline in a toplevel thread", NULL}, - {"stats", 's', POPT_ARG_NONE | POPT_ARGFLAG_STRIP, &stats, 0, - "Show element stats", NULL}, - POPT_TABLEEND - }; - - gst_init_with_popt_table (&argc, &argv, options); - gtk_init (&argc, &argv); - - if (argc != 2) { - g_print ("usage: %s \n", argv[0]); - exit (-1); - } - - pipeline = make_spider_pipeline (argv[1], threaded); - - /* initialize gui elements ... */ - window = gtk_window_new (GTK_WINDOW_TOPLEVEL); - hbox = gtk_hbox_new (FALSE, 0); - vbox = gtk_vbox_new (FALSE, 0); - play_button = gtk_button_new_with_label ("play"); - pause_button = gtk_button_new_with_label ("pause"); - stop_button = gtk_button_new_with_label ("stop"); - - adjustment = - GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.00, 100.0, 0.1, 1.0, 1.0)); - hscale = gtk_hscale_new (adjustment); - gtk_scale_set_digits (GTK_SCALE (hscale), 2); - gtk_range_set_update_policy (GTK_RANGE (hscale), GTK_UPDATE_CONTINUOUS); - - gtk_signal_connect (GTK_OBJECT (hscale), - "button_press_event", G_CALLBACK (start_seek), pipeline); - gtk_signal_connect (GTK_OBJECT (hscale), - "button_release_event", G_CALLBACK (stop_seek), pipeline); - gtk_signal_connect (GTK_OBJECT (hscale), - "format_value", G_CALLBACK (format_value), pipeline); - - /* do the packing stuff ... */ - gtk_window_set_default_size (GTK_WINDOW (window), 96, 96); - gtk_container_add (GTK_CONTAINER (window), vbox); - gtk_container_add (GTK_CONTAINER (vbox), hbox); - gtk_box_pack_start (GTK_BOX (hbox), play_button, FALSE, FALSE, 2); - gtk_box_pack_start (GTK_BOX (hbox), pause_button, FALSE, FALSE, 2); - gtk_box_pack_start (GTK_BOX (hbox), stop_button, FALSE, FALSE, 2); - gtk_box_pack_start (GTK_BOX (vbox), hscale, TRUE, TRUE, 2); - - /* connect things ... */ - g_signal_connect (G_OBJECT (play_button), "clicked", G_CALLBACK (play_cb), - pipeline); - g_signal_connect (G_OBJECT (pause_button), "clicked", G_CALLBACK (pause_cb), - pipeline); - g_signal_connect (G_OBJECT (stop_button), "clicked", G_CALLBACK (stop_cb), - pipeline); - g_signal_connect (G_OBJECT (window), "delete_event", gtk_main_quit, NULL); - - /* show the gui. */ - gtk_widget_show_all (window); - - gtk_main (); - - return 0; -} diff --git a/examples/seeking/vorbisfile.c b/examples/seeking/vorbisfile.c deleted file mode 100644 index d193642fdc..0000000000 --- a/examples/seeking/vorbisfile.c +++ /dev/null @@ -1,266 +0,0 @@ -#include -#include -#include - -static gboolean ready = FALSE; - -struct probe_context -{ - GstElement *pipeline; - GstElement *element; - GstPad *pad; - GstFormat ls_format; - - gint total_ls; - - GstCaps *metadata; - GstCaps *streaminfo; - GstCaps *caps; -}; - -static void -print_caps (GstCaps * caps) -{ - char *s; - - s = gst_caps_to_string (caps); - g_print (" %s\n", s); - g_free (s); -} - -static void -print_format (GstCaps * caps) -{ - char *s; - - s = gst_caps_to_string (caps); - g_print (" format: %s\n", s); - g_free (s); -} - -static void -print_lbs_info (struct probe_context *context, gint stream) -{ - const GstFormat *formats; - - /* FIXME: need a better name here */ - g_print (" stream info:\n"); - - /* report info in all supported formats */ - formats = gst_pad_get_formats (context->pad); - while (*formats) { - const GstFormatDefinition *definition; - gint64 value_start, value_end; - gboolean res; - GstFormat format; - - format = *formats; - formats++; - - if (format == context->ls_format) { - continue; - } - - definition = gst_format_get_details (format); - - /* get start and end position of this stream */ - res = gst_pad_convert (context->pad, - context->ls_format, stream, &format, &value_start); - res &= gst_pad_convert (context->pad, - context->ls_format, stream + 1, &format, &value_end); - - if (res) { - /* substract to get the length */ - value_end -= value_start; - - if (format == GST_FORMAT_TIME) { - value_end /= (GST_SECOND / 100); - g_print (" %s: %" G_GINT64_FORMAT ":%02" G_GINT64_FORMAT ".%02" - G_GINT64_FORMAT "\n", definition->nick, value_end / 6000, - (value_end / 100) % 60, (value_end % 100)); - } else { - g_print (" %s: %" G_GINT64_FORMAT "\n", definition->nick, value_end); - } - } else - g_print (" could not get logical stream %s\n", definition->nick); - - } -} - -static void -deep_notify (GObject * object, GstObject * origin, - GParamSpec * pspec, gpointer data) -{ - struct probe_context *context = (struct probe_context *) data; - GValue value = { 0, }; - - if (!strcmp (pspec->name, "metadata")) { - - g_value_init (&value, pspec->value_type); - g_object_get_property (G_OBJECT (origin), pspec->name, &value); - context->metadata = g_value_peek_pointer (&value); - } else if (!strcmp (pspec->name, "streaminfo")) { - - g_value_init (&value, pspec->value_type); - g_object_get_property (G_OBJECT (origin), pspec->name, &value); - context->streaminfo = g_value_peek_pointer (&value); - } else if (!strcmp (pspec->name, "caps")) { - if (GST_IS_PAD (origin) && GST_PAD (origin) == context->pad) { - g_value_init (&value, pspec->value_type); - g_object_get_property (G_OBJECT (origin), pspec->name, &value); - context->caps = g_value_peek_pointer (&value); - - ready = TRUE; - } - } -} - -static gboolean -collect_logical_stream_properties (struct probe_context *context, gint stream) -{ - GstEvent *event; - gboolean res; - gint count; - - g_print ("info for logical stream %d:\n", stream); - - /* seek to stream */ - event = gst_event_new_seek (context->ls_format | - GST_SEEK_METHOD_SET | GST_SEEK_FLAG_FLUSH, stream); - res = gst_pad_send_event (context->pad, event); - if (!res) { - g_warning ("seek to logical track failed"); - return FALSE; - } - - /* run the pipeline to get the info */ - count = 0; - ready = FALSE; - while (gst_bin_iterate (GST_BIN (context->pipeline)) && !ready) { - count++; - if (count > 10) - break; - } - - print_caps (context->metadata); - print_caps (context->streaminfo); - print_format (context->caps); - print_lbs_info (context, stream); - - g_print ("\n"); - - return TRUE; -} - -static void -collect_stream_properties (struct probe_context *context) -{ - const GstFormat *formats; - - ready = FALSE; - while (gst_bin_iterate (GST_BIN (context->pipeline)) && !ready); - - g_print ("stream info:\n"); - - context->total_ls = -1; - - /* report info in all supported formats */ - formats = gst_pad_get_formats (context->pad); - while (*formats) { - const GstFormatDefinition *definition; - gint64 value; - gboolean res; - GstFormat format; - - format = *formats; - formats++; - - res = gst_pad_query (context->pad, GST_QUERY_TOTAL, &format, &value); - - definition = gst_format_get_details (format); - - if (res) { - if (format == GST_FORMAT_TIME) { - value /= (GST_SECOND / 100); - g_print (" total %s: %" G_GINT64_FORMAT ":%02" G_GINT64_FORMAT ".%02" - G_GINT64_FORMAT "\n", definition->nick, value / 6000, - (value / 100) % 60, (value % 100)); - } else { - if (format == context->ls_format) - context->total_ls = value; - g_print (" total %s: %" G_GINT64_FORMAT "\n", definition->nick, value); - } - } - } - - if (context->total_ls == -1) { - g_warning (" could not get number of logical streams"); - } - g_print ("\n"); -} - -int -main (int argc, char **argv) -{ - GstElement *pipeline; - GstElement *filesrc; - GstElement *vorbisfile; - GstPad *pad; - GstFormat logical_stream_format; - struct probe_context *context; - gint stream; - - gst_init (&argc, &argv); - - if (argc < 2) { - g_print ("usage: %s \n", argv[0]); - return (-1); - } - - pipeline = gst_pipeline_new ("pipeline"); - - filesrc = gst_element_factory_make ("filesrc", "filesrc"); - g_assert (filesrc); - g_object_set (G_OBJECT (filesrc), "location", argv[1], NULL); - - vorbisfile = gst_element_factory_make ("vorbisfile", "vorbisfile"); - //vorbisfile = gst_element_factory_make ("mad", "vorbisfile"); - g_assert (vorbisfile); - - gst_bin_add (GST_BIN (pipeline), filesrc); - gst_bin_add (GST_BIN (pipeline), vorbisfile); - - gst_element_link_pads (filesrc, "src", vorbisfile, "sink"); - - pad = gst_element_get_pad (vorbisfile, "src"); - g_assert (pad); - - logical_stream_format = gst_format_get_by_nick ("logical_stream"); - g_assert (logical_stream_format != 0); - - context = g_new0 (struct probe_context, 1); - context->pipeline = pipeline; - context->element = vorbisfile; - context->pad = pad; - context->ls_format = logical_stream_format; - - g_signal_connect (G_OBJECT (pipeline), "deep_notify", - G_CALLBACK (deep_notify), context); - - gst_element_set_state (pipeline, GST_STATE_PLAYING); - - /* at this point we can inspect the stream */ - collect_stream_properties (context); - - /* loop over all logical streams to get info */ - stream = 0; - while (stream < context->total_ls) { - collect_logical_stream_properties (context, stream); - stream++; - } - - /* stop probe */ - gst_element_set_state (pipeline, GST_STATE_NULL); - - return 0; -} diff --git a/ext/gnomevfs/Makefile.am b/ext/gnomevfs/Makefile.am index 16fa6e40e0..7602fe0adb 100644 --- a/ext/gnomevfs/Makefile.am +++ b/ext/gnomevfs/Makefile.am @@ -3,8 +3,11 @@ plugin_LTLIBRARIES = libgstgnomevfs.la libgstgnomevfs_la_SOURCES = \ gstgnomevfs.c \ gstgnomevfssrc.c \ - gstgnomevfssink.c \ gstgnomevfsuri.c + +EXTRA_DIST = \ + gstgnomevfssink.c + libgstgnomevfs_la_CFLAGS = $(GST_CFLAGS) $(GNOME_VFS_CFLAGS) libgstgnomevfs_la_LIBADD = $(GNOME_VFS_LIBS) libgstgnomevfs_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) diff --git a/ext/gnomevfs/gstgnomevfs.c b/ext/gnomevfs/gstgnomevfs.c index 37c78c63ec..0019b2df90 100644 --- a/ext/gnomevfs/gstgnomevfs.c +++ b/ext/gnomevfs/gstgnomevfs.c @@ -34,10 +34,9 @@ plugin_init (GstPlugin * plugin) { gnome_vfs_init (); - if (!gst_element_register (plugin, "gnomevfssrc", - GST_RANK_SECONDARY, gst_gnomevfssrc_get_type ()) || - !gst_element_register (plugin, "gnomevfssink", - GST_RANK_SECONDARY, gst_gnomevfssink_get_type ())) { + if (!gst_element_register (plugin, "gnomevfssrc", GST_RANK_SECONDARY, gst_gnomevfssrc_get_type ()) /*|| + !gst_element_register (plugin, "gnomevfssink", + GST_RANK_SECONDARY, gst_gnomevfssink_get_type ()) */ ) { return FALSE; } #ifdef ENABLE_NLS diff --git a/ext/gnomevfs/gstgnomevfssrc.c b/ext/gnomevfs/gstgnomevfssrc.c index 7104b97c4b..a157289949 100644 --- a/ext/gnomevfs/gstgnomevfssrc.c +++ b/ext/gnomevfs/gstgnomevfssrc.c @@ -54,7 +54,6 @@ /* gnome-vfs.h doesn't include the following header, which we need: */ #include - #define GST_TYPE_GNOMEVFSSRC \ (gst_gnomevfssrc_get_type()) #define GST_GNOMEVFSSRC(obj) \ @@ -78,37 +77,30 @@ typedef enum } GstGnomeVFSSrcFlags; -typedef struct _GstGnomeVFSSrc GstGnomeVFSSrc; -typedef struct _GstGnomeVFSSrcClass GstGnomeVFSSrcClass; - -struct _GstGnomeVFSSrc +typedef struct _GstGnomeVFSSrc { GstElement element; /* pads */ GstPad *srcpad; - /* uri */ + /* uri, file, ... */ GnomeVFSURI *uri; gchar *uri_name; - - /* handle */ GnomeVFSHandle *handle; gboolean own_handle; + GnomeVFSFileSize size; /* -1 = unknown */ + GnomeVFSFileOffset curoffset; /* current offset in file */ + gboolean seekable; + gulong bytes_per_read; /* bytes per read */ /* Seek stuff */ - gboolean need_flush; - - /* details for fallback synchronous read */ - GnomeVFSFileSize size; - GnomeVFSFileOffset curoffset; /* current offset in file */ - gulong bytes_per_read; /* bytes per read */ - gboolean new_seek; + gboolean need_flush, need_discont; + GnomeVFSFileOffset reqoffset; /* wanted offset for next buf */ /* icecast/audiocast metadata extraction handling */ gboolean iradio_mode; gboolean http_callbacks_pushed; - gboolean seekable; gint icy_metaint; GnomeVFSFileSize icy_count; @@ -126,19 +118,17 @@ struct _GstGnomeVFSSrc gint audiocast_thread_die_outfd; gint audiocast_port; gint audiocast_fd; -}; +} GstGnomeVFSSrc; -struct _GstGnomeVFSSrcClass +typedef struct _GstGnomeVFSSrcClass { GstElementClass parent_class; -}; - -static GstElementDetails gst_gnomevfssrc_details = -GST_ELEMENT_DETAILS ("GnomeVFS Source", - "Source/File", - "Read from any GnomeVFS file", - "Bastien Nocera "); +} GstGnomeVFSSrcClass; +static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); static const GstFormat * gst_gnomevfssrc_get_formats (GstPad * pad) @@ -177,12 +167,6 @@ gst_gnomevfssrc_get_event_mask (GstPad * pad) return masks; } -/* GnomeVFSSrc signals and args */ -enum -{ - LAST_SIGNAL -}; - enum { ARG_0, @@ -193,15 +177,13 @@ enum ARG_IRADIO_NAME, ARG_IRADIO_GENRE, ARG_IRADIO_URL, - ARG_IRADIO_TITLE, - ARG_SEEKABLE + ARG_IRADIO_TITLE }; static void gst_gnomevfssrc_base_init (gpointer g_class); static void gst_gnomevfssrc_class_init (GstGnomeVFSSrcClass * klass); static void gst_gnomevfssrc_init (GstGnomeVFSSrc * gnomevfssrc); static void gst_gnomevfssrc_finalize (GObject * object); - static void gst_gnomevfssrc_uri_handler_init (gpointer g_iface, gpointer iface_data); @@ -210,7 +192,9 @@ static void gst_gnomevfssrc_set_property (GObject * object, guint prop_id, static void gst_gnomevfssrc_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); -static GstData *gst_gnomevfssrc_get (GstPad * pad); +static GstFlowReturn gst_gnomevfssrc_getrange (GstPad * pad, + guint64 offset, guint size, GstBuffer ** buffer); +static gboolean gst_gnomevfssrc_activate (GstPad * pad, GstActivateMode mode); static GstElementStateReturn gst_gnomevfssrc_change_state (GstElement * element); @@ -265,27 +249,36 @@ static void gst_gnomevfssrc_base_init (gpointer g_class) { GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + static GstElementDetails gst_gnomevfssrc_details = + GST_ELEMENT_DETAILS ("GnomeVFS Source", + "Source/File", + "Read from any GnomeVFS-supported file", + "Bastien Nocera \n" + "Ronald S. Bultje "); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&srctemplate)); gst_element_class_set_details (element_class, &gst_gnomevfssrc_details); } static void gst_gnomevfssrc_class_init (GstGnomeVFSSrcClass * klass) { - GObjectClass *gobject_class; - GstElementClass *gstelement_class; - - gobject_class = (GObjectClass *) klass; - gstelement_class = (GstElementClass *) klass; + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); parent_class = g_type_class_ref (GST_TYPE_ELEMENT); + gobject_class->finalize = gst_gnomevfssrc_finalize; + gobject_class->set_property = gst_gnomevfssrc_set_property; + gobject_class->get_property = gst_gnomevfssrc_get_property; + + gstelement_class->change_state = gst_gnomevfssrc_change_state; + + /* properties */ gst_element_class_install_std_props (GST_ELEMENT_CLASS (klass), "bytesperread", ARG_BYTESPERREAD, G_PARAM_READWRITE, "location", ARG_LOCATION, G_PARAM_READWRITE, NULL); - - gobject_class->finalize = gst_gnomevfssrc_finalize; - g_object_class_install_property (gobject_class, ARG_HANDLE, g_param_spec_pointer ("handle", @@ -316,22 +309,16 @@ gst_gnomevfssrc_class_init (GstGnomeVFSSrcClass * klass) g_param_spec_string ("iradio-title", "iradio-title", "Name of currently playing song", NULL, G_PARAM_READABLE)); - g_object_class_install_property (gobject_class, - ARG_SEEKABLE, - g_param_spec_boolean ("seekable", - "seekable", "TRUE is stream is seekable", FALSE, G_PARAM_READABLE)); - - gstelement_class->set_property = gst_gnomevfssrc_set_property; - gstelement_class->get_property = gst_gnomevfssrc_get_property; - - gstelement_class->change_state = gst_gnomevfssrc_change_state; } static void gst_gnomevfssrc_init (GstGnomeVFSSrc * gnomevfssrc) { - gnomevfssrc->srcpad = gst_pad_new ("src", GST_PAD_SRC); - gst_pad_set_get_function (gnomevfssrc->srcpad, gst_gnomevfssrc_get); + gnomevfssrc->srcpad = + gst_pad_new_from_template (gst_static_pad_template_get (&srctemplate), + "src"); + gst_pad_set_getrange_function (gnomevfssrc->srcpad, gst_gnomevfssrc_getrange); + gst_pad_set_activate_function (gnomevfssrc->srcpad, gst_gnomevfssrc_activate); gst_pad_set_event_mask_function (gnomevfssrc->srcpad, gst_gnomevfssrc_get_event_mask); gst_pad_set_event_function (gnomevfssrc->srcpad, @@ -347,14 +334,13 @@ gst_gnomevfssrc_init (GstGnomeVFSSrc * gnomevfssrc) gnomevfssrc->uri = NULL; gnomevfssrc->uri_name = NULL; gnomevfssrc->handle = NULL; - gnomevfssrc->curoffset = 0; + gnomevfssrc->curoffset = gnomevfssrc->reqoffset = 0; gnomevfssrc->bytes_per_read = 4096; - gnomevfssrc->new_seek = FALSE; + gnomevfssrc->need_discont = gnomevfssrc->need_flush = FALSE; + gnomevfssrc->seekable = FALSE; + gnomevfssrc->size = (GnomeVFSFileSize) - 1; gnomevfssrc->icy_metaint = 0; - - gnomevfssrc->seekable = FALSE; - gnomevfssrc->iradio_mode = FALSE; gnomevfssrc->http_callbacks_pushed = FALSE; gnomevfssrc->icy_count = 0; @@ -410,6 +396,10 @@ gst_gnomevfssrc_finalize (GObject * object) G_OBJECT_CLASS (parent_class)->finalize (object); } +/* + * URI interface support. + */ + static guint gst_gnomevfssrc_uri_get_type (void) { @@ -571,9 +561,6 @@ gst_gnomevfssrc_get_property (GObject * object, guint prop_id, GValue * value, g_value_set_string (value, src->iradio_title); g_mutex_unlock (src->audiocast_udpdata_mutex); break; - case ARG_SEEKABLE: - g_value_set_boolean (value, src->seekable); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1018,123 +1005,129 @@ gst_gnomevfssrc_get_icy_metadata (GstGnomeVFSSrc * src) /* end of icecast/audiocast metadata extraction support code */ -/** - * gst_gnomevfssrc_get: - * @pad: #GstPad to push a buffer from - * - * Push a new buffer from the gnomevfssrc at the current offset. +/* + * Read a new buffer from src->reqoffset, takes care of events + * and seeking and such. */ -static GstData * -gst_gnomevfssrc_get (GstPad * pad) +static GstFlowReturn +gst_gnomevfssrc_get (GstGnomeVFSSrc * src, GstBuffer ** buffer) { - GstGnomeVFSSrc *src; - GnomeVFSResult result = 0; + GnomeVFSResult res; GstBuffer *buf; GnomeVFSFileSize readbytes; guint8 *data; - g_return_val_if_fail (pad != NULL, NULL); - src = GST_GNOMEVFSSRC (gst_pad_get_parent (pad)); - g_return_val_if_fail (GST_FLAG_IS_SET (src, GST_GNOMEVFSSRC_OPEN), NULL); + g_return_val_if_fail (GST_FLAG_IS_SET (src, GST_GNOMEVFSSRC_OPEN), + GST_FLOW_ERROR); - /* deal with EOF state */ - if ((src->curoffset >= src->size) && (src->size != 0)) { - gst_element_set_eos (GST_ELEMENT (src)); - GST_DEBUG ("Returning EOS"); - return GST_DATA (gst_event_new (GST_EVENT_EOS)); + /* if the requested offset is outside the file boundary, we´re EOF */ + if (src->size != (GnomeVFSFileSize) - 1 && src->reqoffset >= src->size) { + GST_LOG_OBJECT (src, "Requested offset %lld is outside filesize %llu", + src->reqoffset, src->size); + gst_pad_push_event (src->srcpad, gst_event_new (GST_EVENT_EOS)); + return GST_FLOW_WRONG_STATE; + } + + /* seek if required */ + if (src->curoffset != src->reqoffset) { + if (src->seekable) { + if ((res = gnome_vfs_seek (src->handle, + GNOME_VFS_SEEK_START, src->reqoffset)) != GNOME_VFS_OK) { + GST_ERROR_OBJECT (src, + "Failed to seek to requested position %lld: %s", + src->reqoffset, gnome_vfs_result_to_string (res)); + return GST_FLOW_ERROR; + } + src->curoffset = src->reqoffset; + } else { + GST_ERROR_OBJECT (src, + "Requested seek from %lld to %lld on non-seekable stream", + src->curoffset, src->reqoffset); + return GST_FLOW_ERROR; + } } /* create the buffer */ /* FIXME: should eventually use a bufferpool for this */ buf = gst_buffer_new (); - g_return_val_if_fail (buf, NULL); audiocast_do_notifications (src); if (src->iradio_mode && src->icy_metaint > 0) { - GST_BUFFER_DATA (buf) = g_malloc0 (src->icy_metaint); - data = GST_BUFFER_DATA (buf); - g_return_val_if_fail (GST_BUFFER_DATA (buf) != NULL, NULL); + data = g_malloc (src->icy_metaint); - GST_BUFFER_SIZE (buf) = 0; + /* try to read */ GST_DEBUG ("doing read: icy_count: %" G_GINT64_FORMAT, src->icy_count); - result = gnome_vfs_read (src->handle, data, - src->icy_metaint - src->icy_count, &readbytes); - - /* EOS? */ - if (readbytes == 0) { + if ((res = gnome_vfs_read (src->handle, data, + src->icy_metaint - src->icy_count, + &readbytes)) != GNOME_VFS_OK) { + GST_ERROR_OBJECT (src, "Failed to read iradio data: %s", + gnome_vfs_result_to_string (res)); + return GST_FLOW_ERROR; + } else if (readbytes == 0) { gst_buffer_unref (buf); - GST_DEBUG ("Returning EOS"); - gst_element_set_eos (GST_ELEMENT (src)); - return GST_DATA (gst_event_new (GST_EVENT_EOS)); + GST_LOG_OBJECT (src, "Reading iradio data gave EOS"); + gst_pad_push_event (src->srcpad, gst_event_new (GST_EVENT_EOS)); + return GST_FLOW_WRONG_STATE; } src->icy_count += readbytes; GST_BUFFER_OFFSET (buf) = src->curoffset; GST_BUFFER_SIZE (buf) += readbytes; - data += readbytes; + GST_BUFFER_DATA (buf) = data; src->curoffset += readbytes; + src->reqoffset += readbytes; if (src->icy_count == src->icy_metaint) { gst_gnomevfssrc_get_icy_metadata (src); src->icy_count = 0; } } else { - /* allocate the space for the buffer data */ - GST_BUFFER_DATA (buf) = g_malloc (src->bytes_per_read); - g_return_val_if_fail (GST_BUFFER_DATA (buf) != NULL, NULL); + data = g_malloc (src->bytes_per_read); if (src->need_flush) { - GstEvent *event = gst_event_new_flush (); - src->need_flush = FALSE; - gst_buffer_unref (buf); - GST_DEBUG ("gnomevfssrc sending flush"); - return GST_DATA (event); + GST_LOG_OBJECT (src, "sending flush"); + gst_pad_push_event (src->srcpad, gst_event_new (GST_EVENT_FLUSH)); } - if (src->new_seek) { - GstEvent *event; - - gst_buffer_unref (buf); - GST_DEBUG ("new seek %" G_GINT64_FORMAT, src->curoffset); - src->new_seek = FALSE; - GST_DEBUG ("gnomevfssrc sending discont"); - event = - gst_event_new_discontinuous (FALSE, GST_FORMAT_BYTES, src->curoffset, - NULL); - return GST_DATA (event); + if (src->need_discont) { + src->need_discont = FALSE; + GST_LOG_OBJECT (src, "sending discont"); + gst_pad_push_event (src->srcpad, gst_event_new_discontinuous (FALSE, + GST_FORMAT_BYTES, src->reqoffset, GST_FORMAT_UNDEFINED)); } - result = gnome_vfs_read (src->handle, GST_BUFFER_DATA (buf), - src->bytes_per_read, &readbytes); - - GST_DEBUG ("read: %s, readbytes: %" G_GINT64_FORMAT " @ %" G_GINT64_FORMAT, - gnome_vfs_result_to_string (result), readbytes, src->curoffset); - /* deal with EOS */ - if (readbytes == 0) { + if ((res = gnome_vfs_read (src->handle, data, + src->bytes_per_read, &readbytes)) != GNOME_VFS_OK) { + GST_ERROR_OBJECT (src, "Failed to read data: %s", + gnome_vfs_result_to_string (res)); + return GST_FLOW_ERROR; + } else if (readbytes == 0) { gst_buffer_unref (buf); - GST_DEBUG ("Returning EOS"); - gst_element_set_eos (GST_ELEMENT (src)); - return GST_DATA (gst_event_new (GST_EVENT_EOS)); + GST_LOG_OBJECT (src, "Reading data gave EOS"); + gst_pad_push_event (src->srcpad, gst_event_new (GST_EVENT_EOS)); + return GST_FLOW_WRONG_STATE; } GST_BUFFER_OFFSET (buf) = src->curoffset; GST_BUFFER_SIZE (buf) = readbytes; + GST_BUFFER_DATA (buf) = data; src->curoffset += readbytes; + src->reqoffset += readbytes; } - GST_BUFFER_TIMESTAMP (buf) = -1; - /* we're done, return the buffer */ - return GST_DATA (buf); + *buffer = buf; + + return GST_FLOW_OK; } /* open the file, do stuff necessary to go to READY state */ static gboolean gst_gnomevfssrc_open_file (GstGnomeVFSSrc * src) { - GnomeVFSResult result; + GnomeVFSResult res; GnomeVFSFileInfo *info; g_return_val_if_fail (!GST_FLAG_IS_SET (src, GST_GNOMEVFSSRC_OPEN), FALSE); @@ -1145,42 +1138,43 @@ gst_gnomevfssrc_open_file (GstGnomeVFSSrc * src) gst_gnomevfssrc_push_callbacks (src); if (src->uri != NULL) { - result = gnome_vfs_open_uri (&(src->handle), src->uri, GNOME_VFS_OPEN_READ); - if (result != GNOME_VFS_OK) { + if ((res = gnome_vfs_open_uri (&src->handle, src->uri, + GNOME_VFS_OPEN_READ)) != GNOME_VFS_OK) { gchar *filename = gnome_vfs_uri_to_string (src->uri, GNOME_VFS_URI_HIDE_PASSWORD); gst_gnomevfssrc_pop_callbacks (src); audiocast_thread_kill (src); - GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, - (_("Could not open vfs file \"%s\" for reading."), filename), - (gnome_vfs_result_to_string (result))); + GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), + ("Could not open vfs file \"%s\" for reading: %s", + filename, gnome_vfs_result_to_string (res))); g_free (filename); return FALSE; } src->own_handle = TRUE; } else if (!src->handle) { - GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, - (_("No filename given.")), (NULL)); + GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), ("No filename given")); return FALSE; } else { src->own_handle = FALSE; } + src->size = (GnomeVFSFileSize) - 1; info = gnome_vfs_file_info_new (); - if ((result = gnome_vfs_get_file_info_from_handle (src->handle, - info, GNOME_VFS_FILE_INFO_DEFAULT)) - == GNOME_VFS_OK) { - if (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE) + if ((res = gnome_vfs_get_file_info_from_handle (src->handle, + info, GNOME_VFS_FILE_INFO_DEFAULT)) == GNOME_VFS_OK) { + if (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE) { src->size = info->size; - } else - GST_DEBUG ("getting info failed: %s", gnome_vfs_result_to_string (result)); - + GST_DEBUG_OBJECT (src, "size: %llu bytes", src->size); + } else + GST_LOG_OBJECT (src, "filesize not known"); + } else { + GST_WARNING_OBJECT (src, "getting info failed: %s", + gnome_vfs_result_to_string (res)); + } gnome_vfs_file_info_unref (info); - GST_DEBUG ("size %" G_GINT64_FORMAT, src->size); - audiocast_do_notifications (src); if (gnome_vfs_seek (src->handle, GNOME_VFS_SEEK_CURRENT, 0) @@ -1207,13 +1201,117 @@ gst_gnomevfssrc_close_file (GstGnomeVFSSrc * src) gnome_vfs_close (src->handle); src->handle = NULL; } - src->size = 0; - src->curoffset = 0; - src->new_seek = FALSE; + src->size = (GnomeVFSFileSize) - 1; + src->curoffset = src->reqoffset = 0; + src->need_flush = src->need_discont = FALSE; GST_FLAG_UNSET (src, GST_GNOMEVFSSRC_OPEN); } +/* + * Called to get random access data. + */ + +static GstFlowReturn +gst_gnomevfssrc_getrange (GstPad * pad, + guint64 offset, guint size, GstBuffer ** buffer) +{ + GstGnomeVFSSrc *src = GST_GNOMEVFSSRC (gst_pad_get_parent (pad)); + + /* get ready */ + src->reqoffset = offset; + src->bytes_per_read = size; + + /* get data */ + return gst_gnomevfssrc_get (src, buffer); +} + +/* + * Used if we´re loopbased. + */ + +static void +gst_gnomevfssrc_loop (GstPad * pad) +{ + GstGnomeVFSSrc *src = GST_GNOMEVFSSRC (gst_pad_get_parent (pad)); + GstFlowReturn res; + GstBuffer *buffer; + + if ((res = gst_gnomevfssrc_get (src, &buffer)) != GST_FLOW_OK || + (res = gst_pad_push (pad, buffer)) != GST_FLOW_OK) { + gst_task_pause (GST_RPAD_TASK (pad)); + } +} + +/* + * Called to notify us of scheduling mode. + */ + +static gboolean +gst_gnomevfssrc_activate (GstPad * pad, GstActivateMode mode) +{ + gboolean res = FALSE; + GstGnomeVFSSrc *src = GST_GNOMEVFSSRC (gst_pad_get_parent (pad)); + + switch (mode) { + /* we're the loop function */ + case GST_ACTIVATE_PUSH: + /* if we have a scheduler we can start the task */ + if (GST_ELEMENT_SCHEDULER (src)) { + GST_STREAM_LOCK (pad); + if (!GST_FLAG_IS_SET (src, GST_GNOMEVFSSRC_OPEN)) { + if (!gst_gnomevfssrc_open_file (src)) { + GST_STREAM_UNLOCK (pad); + goto fail_open; + } + } + GST_RPAD_TASK (pad) = + gst_scheduler_create_task (GST_ELEMENT_SCHEDULER (src), + (GstTaskFunction) gst_gnomevfssrc_loop, pad); + + gst_task_start (GST_RPAD_TASK (pad)); + res = TRUE; + GST_STREAM_UNLOCK (pad); + } + break; + + /* random access mode */ + case GST_ACTIVATE_PULL: + if (!GST_FLAG_IS_SET (src, GST_GNOMEVFSSRC_OPEN)) { + if (!gst_gnomevfssrc_open_file (src)) { + goto fail_open; + } + } + res = TRUE; + break; + + case GST_ACTIVATE_NONE: + /* step 1, unblock clock sync (if any) */ + + /* step 2, make sure streaming finishes */ + GST_STREAM_LOCK (pad); + /* step 3, stop the task */ + if (GST_RPAD_TASK (pad)) { + gst_task_stop (GST_RPAD_TASK (pad)); + } + if (GST_FLAG_IS_SET (src, GST_GNOMEVFSSRC_OPEN)) + gst_gnomevfssrc_close_file (src); + GST_STREAM_UNLOCK (pad); + + res = TRUE; + break; + default: + break; + } + + return res; + +fail_open: + { + return FALSE; + } +} + static GstElementStateReturn gst_gnomevfssrc_change_state (GstElement * element) { @@ -1221,17 +1319,8 @@ gst_gnomevfssrc_change_state (GstElement * element) switch (GST_STATE_TRANSITION (element)) { case GST_STATE_READY_TO_PAUSED: - if (!GST_FLAG_IS_SET (element, GST_GNOMEVFSSRC_OPEN)) { - if (!gst_gnomevfssrc_open_file (GST_GNOMEVFSSRC (element))) - return GST_STATE_FAILURE; - } break; case GST_STATE_PAUSED_TO_READY: - if (GST_FLAG_IS_SET (element, GST_GNOMEVFSSRC_OPEN)) - gst_gnomevfssrc_close_file (GST_GNOMEVFSSRC (element)); - break; - case GST_STATE_NULL_TO_READY: - case GST_STATE_READY_TO_NULL: default: break; } @@ -1247,100 +1336,102 @@ gst_gnomevfssrc_srcpad_query (GstPad * pad, GstQueryType type, GstFormat * format, gint64 * value) { GstGnomeVFSSrc *src = GST_GNOMEVFSSRC (gst_pad_get_parent (pad)); + gboolean res = FALSE; switch (type) { case GST_QUERY_TOTAL: - if (*format != GST_FORMAT_BYTES || src->size == 0) { - return FALSE; + if (*format == GST_FORMAT_BYTES || src->size != (GnomeVFSFileSize) - 1) { + res = TRUE; + *value = src->size; } - *value = src->size; break; case GST_QUERY_POSITION: switch (*format) { case GST_FORMAT_BYTES: *value = src->curoffset; + res = TRUE; break; case GST_FORMAT_PERCENT: - if (src->size == 0) - return FALSE; - *value = src->curoffset * GST_FORMAT_PERCENT_MAX / src->size; + if (src->size != (GnomeVFSFileSize) - 1) { + res = TRUE; + *value = src->curoffset * GST_FORMAT_PERCENT_MAX / src->size; + } break; default: - return FALSE; + break; } break; default: - return FALSE; break; } - return TRUE; + + return res; } static gboolean gst_gnomevfssrc_srcpad_event (GstPad * pad, GstEvent * event) { GstGnomeVFSSrc *src = GST_GNOMEVFSSRC (GST_PAD_PARENT (pad)); + gboolean res = FALSE; switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEEK: { gint64 desired_offset; - GnomeVFSResult result; + GnomeVFSResult res; if (GST_EVENT_SEEK_FORMAT (event) != GST_FORMAT_BYTES) { - gst_event_unref (event); - return FALSE; + break; + } else if (!src->seekable) { + GST_WARNING_OBJECT (src, "Seek on non-seekable stream"); + break; } + switch (GST_EVENT_SEEK_METHOD (event)) { case GST_SEEK_METHOD_SET: - desired_offset = (guint64) GST_EVENT_SEEK_OFFSET (event); + desired_offset = GST_EVENT_SEEK_OFFSET (event); break; case GST_SEEK_METHOD_CUR: desired_offset = src->curoffset + GST_EVENT_SEEK_OFFSET (event); break; case GST_SEEK_METHOD_END: - if (src->size == 0) - return FALSE; + if (src->size == (GnomeVFSFileSize) - 1) { + goto done; + } desired_offset = src->size - ABS (GST_EVENT_SEEK_OFFSET (event)); break; default: - gst_event_unref (event); - return FALSE; - break; + goto done; } - result = gnome_vfs_seek (src->handle, - GNOME_VFS_SEEK_START, desired_offset); - GST_DEBUG ("new_seek to %" G_GINT64_FORMAT ": %s", - desired_offset, gnome_vfs_result_to_string (result)); - - if (result != GNOME_VFS_OK) { - gst_event_unref (event); - return FALSE; + /* check boundaries */ + if (desired_offset < 0 || desired_offset >= src->size) { + GST_WARNING_OBJECT (src, "Seek to %lld is outside filebounds 0-%llu", + desired_offset, src->size); + break; } - src->curoffset = desired_offset; - src->new_seek = TRUE; + + /* prepare for seek */ + src->reqoffset = desired_offset; + GST_DEBUG_OBJECT (src, "new_seek to %lld: %s", desired_offset); + src->need_discont = TRUE; src->need_flush = GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH; + res = TRUE; break; } case GST_EVENT_SIZE: if (GST_EVENT_SIZE_FORMAT (event) != GST_FORMAT_BYTES) { - gst_event_unref (event); - return FALSE; + break; } src->bytes_per_read = GST_EVENT_SIZE_VALUE (event); g_object_notify (G_OBJECT (src), "bytesperread"); break; - - case GST_EVENT_FLUSH: - src->need_flush = TRUE; - break; default: - gst_event_unref (event); - return FALSE; break; } + +done: gst_event_unref (event); - return TRUE; + return res; } diff --git a/ext/ogg/README b/ext/ogg/README index 2b4bc64b48..b9ec119ee1 100644 --- a/ext/ogg/README +++ b/ext/ogg/README @@ -1,9 +1,110 @@ +ogg demuxer +----------- + +This ogg demuxer has two modes of operation, which both share a significant +amount of code. The first mode is the streaming mode which is automatically +selected when the demuxer is connected to a non-getrange based element. When +connected to a getrange based element the ogg demuxer can do full seeking +with great efficiency. + +1) the streaming mode. + + In this mode, the ogg demuxer receives buffers in the _chain() function which + are then simply submited to the ogg sync layer. Pages are then processed when the + sync layer detects them, pads are created for new chains and packets are sent to + the peer elements of the pads. + + In this mode, no seeking is possible. This is the typical case when the stream is + read from a network source. + + In this mode, no setup is done at startup, the pages are just read and decoded. + A new logical chain is detected when one of the pages has the BOS flag set. At this + point the existing pads are removed and new pads are created for all the logical + streams in this new chain. + + +2) the random access mode. + + In this mode, the ogg file is first scanned to detect the position and length of + all chains. This scanning is performed using a recursive binary search algorithm + that is explained below. + + find_chains(start, end) + { + ret1 = read_next_pages (start); + ret2 = read_prev_page (end); + + if (WAS_HEADER (ret1)) { + } + else { + } + + } + + a) read first and last pages + + start end + V V + +-----------------------+-------------+--------------------+ + | 111 | 222 | 333 | + BOS BOS BOS EOS + + + after reading start, serial 111, BOS, chain[0] = 111 + after reading end, serial 333, EOS + + start serialno != end serialno, binary search start, (end-start)/2 + + start bisect end + V V V + +-----------------------+-------------+--------------------+ + | 111 | 222 | 333 | + + + after reading start, serial 111, BOS, chain[0] = 111 + after reading end, serial 222, EOS + + while ( + + + +testcases +--------- + + a) stream without BOS + + +----------------------------------------------------------+ + 111 | + EOS + + b) chained stream, first chain without BOS + + +-------------------+--------------------------------------+ + 111 | 222 | + BOS EOS + + + c) chained stream + + +-------------------+--------------------------------------+ + | 111 | 222 | + BOS BOS EOS + + + d) chained stream, second without BOS + + +-------------------+--------------------------------------+ + | 111 | 222 | + BOS EOS + + + ogg and the granulepos ---------------------- an ogg streams contains pages with a serial number and a granule pos. The granulepos -is a number that is codec specific and denotes the 'position' of the last packet -in that page. +is a number that is codec specific and denotes the 'position' of the last sample in +the last packet in that page. ogg has therefore no notion about time, it only knows about bytes and granule positions. diff --git a/ext/ogg/gstoggdemux.c b/ext/ogg/gstoggdemux.c index 2b66a913f2..faaa11a66b 100644 --- a/ext/ogg/gstoggdemux.c +++ b/ext/ogg/gstoggdemux.c @@ -1,5 +1,5 @@ /* GStreamer - * Copyright (C) 2003, 2004 Benjamin Otte + * Copyright (C) 2004 Wim Taymans * * gstoggdemux.c: ogg stream demuxer * @@ -23,140 +23,150 @@ #include "config.h" #endif #include -#include #include #include -/* tweak this to improve setup times */ -/* PLEASE don't just tweak it because one file is faster with tweaked numbers, - * but use a good benchmark with both video and audio files */ -/* number of bytes we seek in front of desired point so we can resync properly */ -#define SETUP_EXPECTED_PAGE_SIZE (8500) /* this is out of vorbisfile */ -/* number of bytes where we don't seek to middle anymore but just walk through - * all packets */ -#define SETUP_PASSTHROUGH_SIZE (SETUP_EXPECTED_PAGE_SIZE * 20) -/* if we have to repeat a seek backwards because we didn't seek back far enough, - * we multiply the amount we seek by this amount */ -#define SETUP_SEEK_MULTIPLIER (5) +#define CHUNKSIZE (8500) /* this is out of vorbisfile */ +enum +{ + OV_EREAD = -1, + OV_EFAULT = -2, + OV_FALSE = -3, + OV_EOF = -4, +}; GST_DEBUG_CATEGORY_STATIC (gst_ogg_demux_debug); GST_DEBUG_CATEGORY_STATIC (gst_ogg_demux_setup_debug); #define GST_CAT_DEFAULT gst_ogg_demux_debug +#define GST_TYPE_OGG_PAD (gst_ogg_pad_get_type()) +#define GST_OGG_PAD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OGG_PAD, GstOggPad)) +#define GST_OGG_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OGG_PAD, GstOggPad)) +#define GST_IS_OGG_PAD(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OGG_PAD)) +#define GST_IS_OGG_PAD_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OGG_PAD)) + +typedef struct _GstOggPad GstOggPad; +typedef struct _GstOggPadClass GstOggPadClass; + #define GST_TYPE_OGG_DEMUX (gst_ogg_demux_get_type()) #define GST_OGG_DEMUX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OGG_DEMUX, GstOggDemux)) #define GST_OGG_DEMUX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OGG_DEMUX, GstOggDemux)) #define GST_IS_OGG_DEMUX(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OGG_DEMUX)) #define GST_IS_OGG_DEMUX_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OGG_DEMUX)) +static GType gst_ogg_demux_get_type (void); + typedef struct _GstOggDemux GstOggDemux; typedef struct _GstOggDemuxClass GstOggDemuxClass; +/* all information needed for one ogg chain (relevant for chained bitstreams) */ +typedef struct _GstOggChain +{ + GstOggDemux *ogg; + + gint64 offset; /* starting offset of chain */ + gint64 end_offset; /* end offset of chain */ + gint64 bytes; /* number of bytes */ + + gboolean have_bos; + + GArray *streams; + + GstClockTime total_time; /* the total time of this chain, this is the MAX of + the totals of all streams */ + GstClockTime start_time; /* the timestamp of the first sample */ + GstClockTime last_time; /* the timestamp of the last page == last sample */ + + GstClockTime begin_time; /* when this chain starts in the stream */ + +} GstOggChain; + +/* different modes for the pad */ typedef enum { - /* just because you shouldn't make a valid enum value 0 */ - GST_OGG_STATE_INAVLID, - /* just started, we need to decide if we should do setup */ - GST_OGG_STATE_START, - /* setup is analyzing the stream, getting lengths and so on */ - GST_OGG_STATE_SETUP, - /* after a seek, during resyncing */ - GST_OGG_STATE_SEEK, - /* normal playback */ - GST_OGG_STATE_PLAY -} -GstOggState; + GST_OGG_PAD_MODE_INIT, /* we are feeding our internal decoder to get info */ + GST_OGG_PAD_MODE_STREAMING, /* we are streaming buffers to the outside */ +} GstOggPadMode; /* all information needed for one ogg stream */ -typedef struct +struct _GstOggPad { - GstPad *pad; /* reference for this pad is held by element we belong to */ + GstRealPad pad; /* subclass GstRealPad */ + + GstOggPadMode mode; + + GstPad *elem_pad; /* sinkpad of internal element */ + GstElement *element; /* internal element */ + GstPad *elem_out; /* our sinkpad to receive buffers form the internal element */ + + GstOggChain *chain; /* the chain we are part of */ + GstOggDemux *ogg; /* the ogg demuxer we are part of */ + + GList *headers; + + gint serialno; + gint64 packetno; + gint64 offset; + + GstEvent *new_segment; + gint64 start; + gint64 stop; + + gint64 current_granule; + + GstClockTime start_time; /* the timestamp of the first sample */ + + gint64 first_granule; /* the granulepos of first page == first sample in next page */ + GstClockTime first_time; /* the timestamp of the second page */ + + GstClockTime last_time; /* the timestamp of the last page == last sample */ + gint64 last_granule; /* the granulepos of the last page */ + + GstClockTime total_time; /* the total time of this stream */ - gint serial; ogg_stream_state stream; - guint64 offset; /* end offset of last buffer */ - guint64 known_offset; /* last known offset */ - gint64 packetno; /* number of next expected packet */ +}; - guint64 start; /* first valid granulepos */ - guint64 length; /* length of stream or 0 */ - glong pages; /* number of pages in stream or 0 */ - - gint64 start_offset; /* earliest offset in file where this stream has been found */ - gboolean start_found; /* we have found the bos (first) page */ - gint64 end_offset; /* last offset in file where this stream has been found */ - gboolean end_found; /* we have fount the eos (last) page */ - - guint flags; -} -GstOggPad; +struct _GstOggPadClass +{ + GstRealPadClass parent_class; +}; typedef enum { - GST_OGG_PAD_NEEDS_DISCONT = (1 << 0), - GST_OGG_PAD_NEEDS_FLUSH = (1 << 1) -} -GstOggPadFlags; + OGG_STATE_NEW_CHAIN, + OGG_STATE_STREAMING, +} OggState; -/* all information needed for one ogg chain (relevant for chained bitstreams) */ -typedef struct -{ - gint64 starts_at; /* starting offset of chain */ - gint64 ends_at; /* end offset of stream (only valid when not last chain or not in setup) */ - - GSList *pads; /* list of GstOggPad */ -} -GstOggChain; - -#define CURRENT_CHAIN(ogg) (&g_array_index ((ogg)->chains, GstOggChain, (ogg)->current_chain)) -#define FOR_PAD_IN_CURRENT_CHAIN(ogg, __pad, ...) \ - FOR_PAD_IN_CHAIN(ogg, __pad, (ogg)->current_chain, __VA_ARGS__) -#define FOR_PAD_IN_CHAIN(ogg, _pad, i, ...) G_STMT_START{ \ - GSList *_walk; \ - GstOggChain *_chain = &g_array_index ((ogg)->chains, GstOggChain, i); \ - if (i != -1) { \ - for (_walk = _chain->pads; _walk; _walk = g_slist_next (_walk)) { \ - GstOggPad *_pad = (GstOggPad *) _walk->data; \ - __VA_ARGS__ \ - } \ - } \ -}G_STMT_END - -typedef enum -{ - GST_OGG_FLAG_BOS = GST_ELEMENT_FLAG_LAST, - GST_OGG_FLAG_EOS, - GST_OGG_FLAG_WAIT_FOR_DISCONT -} -GstOggFlag; +#define GST_CHAIN_LOCK(ogg) g_mutex_lock((ogg)->chain_lock) +#define GST_CHAIN_UNLOCK(ogg) g_mutex_unlock((ogg)->chain_lock) struct _GstOggDemux { GstElement element; - /* pad */ - GstFilePad *sinkpad; + GstPad *sinkpad; + + gint64 length; + gint64 offset; + + OggState state; + gboolean seekable; + + gboolean need_chains; /* state */ - GstOggState state; + GMutex *chain_lock; /* we need the lock to protect the chains */ GArray *chains; /* list of chains we know */ - gint current_chain; /* id of chain that currently "plays" */ - gboolean bos; /* no-more-pads signal needs this */ - /* setup */ - GSList *unordered; /* streams we haven't found chains for yet */ - guint setup_state; /* seperate from global state */ + + GstClockTime total_time; /* the total time of this ogg, this is the sum of + the totals of all chains */ + GstOggChain *current_chain; + GstOggChain *building_chain; /* ogg stuff */ ogg_sync_state sync; - - /* seeking */ - GstOggPad *seek_pad; - gint64 seek_to; - gint64 seek_skipped; - guint64 seek_offset; - GstFormat seek_format; - gint seek_try; }; struct _GstOggDemuxClass @@ -164,6 +174,681 @@ struct _GstOggDemuxClass GstElementClass parent_class; }; +static gboolean gst_ogg_demux_perform_seek (GstOggDemux * ogg, gint64 pos); + +static void gst_ogg_pad_class_init (GstOggPadClass * klass); +static void gst_ogg_pad_init (GstOggPad * pad); +static void gst_ogg_pad_dispose (GObject * object); +static void gst_ogg_pad_finalize (GObject * object); +static const GstFormat *gst_ogg_pad_formats (GstPad * pad); +static const GstEventMask *gst_ogg_pad_event_masks (GstPad * pad); +static const GstQueryType *gst_ogg_pad_query_types (GstPad * pad); +static gboolean gst_ogg_pad_src_convert (GstPad * pad, + GstFormat src_format, gint64 src_value, + GstFormat * dest_format, gint64 * dest_value); +static gboolean gst_ogg_pad_src_query (GstPad * pad, GstQueryType type, + GstFormat * format, gint64 * value); +static gboolean gst_ogg_pad_event (GstPad * pad, GstEvent * event); +static GstCaps *gst_ogg_pad_getcaps (GstPad * pad); +static GstCaps *gst_ogg_type_find (ogg_packet * packet); + +static GstRealPadClass *ogg_pad_parent_class = NULL; + +static GType +gst_ogg_pad_get_type (void) +{ + static GType ogg_pad_type = 0; + + if (!ogg_pad_type) { + static const GTypeInfo ogg_pad_info = { + sizeof (GstOggPadClass), + NULL, + NULL, + (GClassInitFunc) gst_ogg_pad_class_init, + NULL, + NULL, + sizeof (GstOggPad), + 0, + (GInstanceInitFunc) gst_ogg_pad_init, + }; + + ogg_pad_type = + g_type_register_static (GST_TYPE_REAL_PAD, "GstOggPad", &ogg_pad_info, + 0); + } + return ogg_pad_type; +} + +static void +gst_ogg_pad_class_init (GstOggPadClass * klass) +{ + GObjectClass *gobject_class; + + gobject_class = (GObjectClass *) klass; + + ogg_pad_parent_class = g_type_class_ref (G_TYPE_OBJECT); + + gobject_class->dispose = gst_ogg_pad_dispose; + gobject_class->finalize = gst_ogg_pad_finalize; +} + +static void +gst_ogg_pad_init (GstOggPad * pad) +{ + gst_pad_set_event_function (GST_PAD (pad), + GST_DEBUG_FUNCPTR (gst_ogg_pad_event)); + gst_pad_set_event_mask_function (GST_PAD (pad), + GST_DEBUG_FUNCPTR (gst_ogg_pad_event_masks)); + gst_pad_set_getcaps_function (GST_PAD (pad), + GST_DEBUG_FUNCPTR (gst_ogg_pad_getcaps)); + gst_pad_set_query_type_function (GST_PAD (pad), + GST_DEBUG_FUNCPTR (gst_ogg_pad_query_types)); + gst_pad_set_formats_function (GST_PAD (pad), + GST_DEBUG_FUNCPTR (gst_ogg_pad_formats)); + gst_pad_set_convert_function (GST_PAD (pad), + GST_DEBUG_FUNCPTR (gst_ogg_pad_src_convert)); + gst_pad_set_query_function (GST_PAD (pad), + GST_DEBUG_FUNCPTR (gst_ogg_pad_src_query)); + + pad->mode = GST_OGG_PAD_MODE_INIT; + + pad->first_granule = -1; + pad->last_granule = -1; + pad->current_granule = -1; + + pad->start_time = -1; + pad->first_time = -1; + pad->last_time = -1; + + pad->headers = NULL; +} + +static void +gst_ogg_pad_dispose (GObject * object) +{ + GstOggPad *pad = GST_OGG_PAD (object); + + gst_object_replace ((GstObject **) (&pad->elem_pad), NULL); + gst_object_replace ((GstObject **) (&pad->element), NULL); + gst_object_replace ((GstObject **) (&pad->elem_out), NULL); + + pad->chain = NULL; + pad->ogg = NULL; + + g_list_foreach (pad->headers, (GFunc) gst_data_unref, NULL); + g_list_free (pad->headers); + pad->headers = NULL; + + if (pad->new_segment) { + gst_event_unref (pad->new_segment); + pad->new_segment = NULL; + } + ogg_stream_reset (&pad->stream); + + G_OBJECT_CLASS (ogg_pad_parent_class)->dispose (object); +} + +static void +gst_ogg_pad_finalize (GObject * object) +{ + GstOggPad *pad = GST_OGG_PAD (object); + + ogg_stream_clear (&pad->stream); + + G_OBJECT_CLASS (ogg_pad_parent_class)->finalize (object); +} + +static const GstFormat * +gst_ogg_pad_formats (GstPad * pad) +{ + static GstFormat src_formats[] = { + GST_FORMAT_DEFAULT, /* time */ + GST_FORMAT_TIME, /* granulepos */ + 0 + }; + static GstFormat sink_formats[] = { + GST_FORMAT_BYTES, + GST_FORMAT_DEFAULT, /* bytes */ + 0 + }; + + return (GST_PAD_IS_SRC (pad) ? src_formats : sink_formats); +} + +static const GstEventMask * +gst_ogg_pad_event_masks (GstPad * pad) +{ + static const GstEventMask src_event_masks[] = { + {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_FLUSH}, + {0,} + }; + + return src_event_masks; +} + +static const GstQueryType * +gst_ogg_pad_query_types (GstPad * pad) +{ + static const GstQueryType query_types[] = { + GST_QUERY_POSITION, + GST_QUERY_TOTAL, + 0 + }; + + return query_types; +} + +static GstCaps * +gst_ogg_pad_getcaps (GstPad * pad) +{ + return gst_caps_ref (GST_PAD_CAPS (pad)); +} + +static gboolean +gst_ogg_pad_src_convert (GstPad * pad, + GstFormat src_format, gint64 src_value, + GstFormat * dest_format, gint64 * dest_value) +{ + gboolean res = FALSE; + GstOggDemux *ogg; + + ogg = GST_OGG_DEMUX (GST_PAD_PARENT (pad)); + + /* fill me, not sure with what... */ + + return res; +} + +static gboolean +gst_ogg_pad_src_query (GstPad * pad, GstQueryType type, + GstFormat * format, gint64 * value) +{ + gboolean res = TRUE; + GstOggDemux *ogg; + GstOggPad *cur; + + ogg = GST_OGG_DEMUX (GST_PAD_PARENT (pad)); + cur = GST_OGG_PAD (pad); + + switch (type) { + case GST_QUERY_POSITION: + *value = cur->current_granule; + break; + case GST_QUERY_TOTAL: + *value = ogg->total_time; + break; + default: + res = FALSE; + break; + } + + return res; +} + +static gboolean +gst_ogg_pad_event (GstPad * pad, GstEvent * event) +{ + gboolean res; + GstOggDemux *ogg; + GstOggPad *cur; + + ogg = GST_OGG_DEMUX (GST_PAD_PARENT (pad)); + cur = GST_OGG_PAD (pad); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEEK: + { + gint64 offset; + + /* can't seek if we are not seekable */ + if (!ogg->seekable) { + res = FALSE; + goto done_unref; + } + /* we can only seek on time */ + if (GST_EVENT_SEEK_FORMAT (event) != GST_FORMAT_TIME) { + res = FALSE; + goto done_unref; + } + offset = GST_EVENT_SEEK_OFFSET (event); + gst_event_unref (event); + + /* now do the seek */ + res = gst_ogg_demux_perform_seek (ogg, offset); + break; + } + default: + res = gst_pad_event_default (pad, event); + break; + } + + return res; + +done_unref: + gst_event_unref (event); + return res; +} + +static void +gst_ogg_pad_reset (GstOggPad * pad) +{ + ogg_stream_reset (&pad->stream); + /* FIXME: need a discont here */ +} + +/* the filter function for selecting the elements we can use in + * * autoplugging */ +static gboolean +gst_ogg_demux_factory_filter (GstPluginFeature * feature, GstCaps * caps) +{ + guint rank; + const gchar *klass; + + /* we only care about element factories */ + if (!GST_IS_ELEMENT_FACTORY (feature)) + return FALSE; + + klass = gst_element_factory_get_klass (GST_ELEMENT_FACTORY (feature)); + /* only demuxers and decoders can play */ + if (strstr (klass, "Demux") == NULL && + strstr (klass, "Decoder") == NULL && strstr (klass, "Parse") == NULL) { + return FALSE; + } + + /* only select elements with autoplugging rank */ + rank = gst_plugin_feature_get_rank (feature); + if (rank < GST_RANK_MARGINAL) + return FALSE; + + GST_DEBUG ("checking factory %s", GST_PLUGIN_FEATURE_NAME (feature)); + /* now see if it is compatible with the caps */ + { + GstElementFactory *factory = GST_ELEMENT_FACTORY (feature); + const GList *templates; + GList *walk; + + /* get the templates from the element factory */ + templates = gst_element_factory_get_pad_templates (factory); + + for (walk = (GList *) templates; walk; walk = g_list_next (walk)) { + GstPadTemplate *templ = GST_PAD_TEMPLATE (walk->data); + + /* we only care about the sink templates */ + if (templ->direction == GST_PAD_SINK) { + GstCaps *intersect; + + /* try to intersect the caps with the caps of the template */ + intersect = + gst_caps_intersect (caps, gst_pad_template_get_caps (templ)); + + /* check if the intersection is empty */ + if (!gst_caps_is_empty (intersect)) { + /* non empty intersection, we can use this element */ + gst_caps_unref (intersect); + goto found; + } + gst_caps_unref (intersect); + } + } + } + return FALSE; + +found: + return TRUE; +} + +/* function used to sort element features */ +static gint +compare_ranks (GstPluginFeature * f1, GstPluginFeature * f2) +{ + gint diff; + + diff = gst_plugin_feature_get_rank (f2) - gst_plugin_feature_get_rank (f1); + if (diff != 0) + return diff; + return strcmp (gst_plugin_feature_get_name (f2), + gst_plugin_feature_get_name (f1)); +} + +static GstFlowReturn +gst_ogg_pad_internal_chain (GstPad * pad, GstBuffer * buffer) +{ + GstOggPad *oggpad; + GstClockTime timestamp; + + oggpad = gst_pad_get_element_private (pad); + + timestamp = GST_BUFFER_TIMESTAMP (buffer); + GST_DEBUG_OBJECT (oggpad, "received buffer from iternal pad, TS=%lld", + timestamp); + + if (oggpad->start_time == -1) + oggpad->start_time = timestamp; + + return GST_FLOW_OK; +} + +/* runs typefind on the packet, which is assumed to be the first + * packet in the stream. + * + * Based on the type returned from the typefind function, an element + * is created to help in conversion between granulepos and timestamps + * so that we can do decent seeking. + */ +static gboolean +gst_ogg_pad_typefind (GstOggPad * pad, ogg_packet * packet) +{ + GstCaps *caps; + GstElement *element = NULL; + GstOggDemux *ogg = pad->ogg; + + if (GST_PAD_CAPS (pad) != NULL) + return TRUE; + + caps = gst_ogg_type_find (packet); + + if (caps == NULL) { + GST_WARNING_OBJECT (ogg, + "couldn't find caps for stream with serial %08lx", pad->serialno); + caps = gst_caps_new_simple ("application/octet-stream", NULL); + } else { + /* ogg requires you to use a decoder element to define the + * meaning of granulepos etc so we make one. We only do this if + * we are in the seeking mode. */ + if (ogg->seekable) { + GList *factories; + + /* first filter out the interesting element factories */ + factories = gst_registry_pool_feature_filter ( + (GstPluginFeatureFilter) gst_ogg_demux_factory_filter, FALSE, caps); + + /* sort them according to their ranks */ + factories = g_list_sort (factories, (GCompareFunc) compare_ranks); + + /* then pick the first factory to create an element */ + if (factories) { + element = + gst_element_factory_create (GST_ELEMENT_FACTORY (factories->data), + NULL); + if (element) { + /* this is ours */ + gst_object_ref (GST_OBJECT (element)); + gst_object_sink (GST_OBJECT (element)); + + /* FIXME, it might not be named "sink" */ + pad->elem_pad = gst_element_get_pad (element, "sink"); + gst_element_set_state (element, GST_STATE_PAUSED); + pad->elem_out = gst_pad_new ("internal", GST_PAD_SINK); + gst_pad_set_chain_function (pad->elem_out, + gst_ogg_pad_internal_chain); + gst_pad_set_element_private (pad->elem_out, pad); + gst_pad_set_caps (pad->elem_out, gst_caps_new_any ()); + gst_pad_set_active (pad->elem_out, TRUE); + + /* and this pad may not be named src.. */ + gst_pad_link (gst_element_get_pad (element, "src"), pad->elem_out); + } + } + g_list_free (factories); + } else { + pad->mode = GST_OGG_PAD_MODE_STREAMING; + } + } + pad->element = element; + + gst_pad_set_caps (GST_PAD (pad), caps); + gst_caps_unref (caps); + + return TRUE; +} + +/* submit a packet to the oggpad, this function will run the + * typefind code for the pad if this is the first packet for this + * stream + */ +static GstFlowReturn +gst_ogg_pad_submit_packet (GstOggPad * pad, ogg_packet * packet) +{ + GstBuffer *buf; + gint64 granule; + GstFlowReturn ret = GST_FLOW_OK; + + GstOggDemux *ogg = pad->ogg; + + GST_DEBUG_OBJECT (ogg, + "%p submit packet serial %08lx, packetno %lld", pad, pad->serialno, + pad->packetno); + + granule = packet->granulepos; + if (granule != -1) { + pad->current_granule = granule; + if (pad->first_granule == -1 && granule != 0) { + pad->first_granule = granule; + } + } + /* first packet, FIXME, do this in chain activation */ + if (pad->packetno == 0) { + gst_ogg_pad_typefind (pad, packet); + } +#if 0 + if (ogg->state != OGG_STATE_STREAMING) { + GST_DEBUG_OBJECT (ogg, "%p collecting headers, state %d", pad, ogg->state); + + buf = gst_buffer_new_and_alloc (packet->bytes); + memcpy (GST_BUFFER_DATA (buf), packet->packet, packet->bytes); + gst_buffer_set_caps (buf, GST_PAD_CAPS (pad)); + GST_BUFFER_OFFSET (buf) = -1; + GST_BUFFER_OFFSET_END (buf) = packet->granulepos; + + /* we are collecting the chain info, just need to queue the buffers */ + pad->headers = g_list_append (pad->headers, buf); + + goto done; + } +#endif + + /* stream packet to peer plugin */ + if (pad->mode == GST_OGG_PAD_MODE_STREAMING) { + buf = + gst_pad_alloc_buffer (GST_PAD (pad), GST_BUFFER_OFFSET_NONE, + packet->bytes, GST_PAD_CAPS (pad)); + + GST_DEBUG_OBJECT (ogg, + "%p streaming to peer serial %08lx, packetno %lld", pad, pad->serialno, + pad->packetno); + + if (pad->new_segment) { + ret = gst_pad_push_event (GST_PAD (pad), pad->new_segment); + pad->new_segment = NULL; + } + if (buf) { + memcpy (buf->data, packet->packet, packet->bytes); + + pad->offset = packet->granulepos; + GST_BUFFER_OFFSET (buf) = -1; + GST_BUFFER_OFFSET_END (buf) = packet->granulepos; + + ret = gst_pad_push (GST_PAD (pad), buf); + } else { + GST_DEBUG_OBJECT (ogg, + "%p could not get buffer from peer %08lx, packetno %lld", pad, + pad->serialno, pad->packetno); + } + } else { + /* initialize our internal decoder with packets */ + if (!pad->elem_pad) { + GST_WARNING_OBJECT (ogg, + "pad %08lx does not have elem_pad, no decoder ?", pad); + return GST_FLOW_OK; + } + + GST_DEBUG_OBJECT (ogg, + "%p init decoder serial %08lx, packetno %lld", pad, pad->serialno, + pad->packetno); + + buf = gst_buffer_new_and_alloc (packet->bytes); + memcpy (GST_BUFFER_DATA (buf), packet->packet, packet->bytes); + gst_buffer_set_caps (buf, GST_PAD_CAPS (pad)); + GST_BUFFER_OFFSET (buf) = -1; + GST_BUFFER_OFFSET_END (buf) = packet->granulepos; + + ret = GST_RPAD_CHAINFUNC (pad->elem_pad) (pad->elem_pad, buf); + } + + pad->packetno++; + + return ret; +} + +/* submit a page to an oggpad, this function will then submit all + * the packets in the page. + */ +static GstFlowReturn +gst_ogg_pad_submit_page (GstOggPad * pad, ogg_page * page) +{ + ogg_packet packet; + int ret; + gboolean done = FALSE; + GstFlowReturn result = GST_FLOW_OK; + GstOggDemux *ogg; + + ogg = GST_OGG_DEMUX (GST_PAD_PARENT (pad)); + + if (ogg_stream_pagein (&pad->stream, page) != 0) { + GST_WARNING_OBJECT (ogg, + "ogg stream choked on page (serial %08lx), resetting stream", + pad->serialno); + gst_ogg_pad_reset (pad); + return GST_FLOW_OK; + } + + while (!done) { + ret = ogg_stream_packetout (&pad->stream, &packet); + GST_LOG_OBJECT (ogg, "packetout gave %d", ret); + switch (ret) { + case 0: + done = TRUE; + break; + case -1: + /* out of sync, could call gst_ogg_pad_reset() here but ogg can decode + * the packet just fine. We should probably send a DISCONT though. */ + break; + case 1: + result = gst_ogg_pad_submit_packet (pad, &packet); + if (result != GST_FLOW_OK) { + GST_WARNING_OBJECT (ogg, "could not submit packet, error: %d", + result); + gst_ogg_pad_reset (pad); + done = TRUE; + } + break; + default: + GST_WARNING_OBJECT (ogg, + "invalid return value %d for ogg_stream_packetout, resetting stream", + ret); + gst_ogg_pad_reset (pad); + break; + } + } + return result; +} + + +static GstOggChain * +gst_ogg_chain_new (GstOggDemux * ogg) +{ + GstOggChain *chain = g_new0 (GstOggChain, 1); + + GST_DEBUG_OBJECT (ogg, "creating new chain %p", chain); + chain->ogg = ogg; + chain->offset = -1; + chain->bytes = -1; + chain->have_bos = FALSE; + chain->streams = g_array_new (FALSE, TRUE, sizeof (GstOggPad *)); + + return chain; +} + +static void +gst_ogg_chain_free (GstOggChain * chain) +{ + gint i; + + for (i = 0; i < chain->streams->len; i++) { + GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i); + + gst_object_unref (GST_OBJECT (pad)); + } + g_array_free (chain->streams, TRUE); + chain->streams = NULL; +} + +static GstOggPad * +gst_ogg_chain_new_stream (GstOggChain * chain, glong serialno) +{ + GstOggPad *ret; + GstTagList *list; + gchar *name; + + GST_DEBUG_OBJECT (chain->ogg, "creating new stream %08lx in chain %p", + serialno, chain); + + ret = g_object_new (GST_TYPE_OGG_PAD, NULL); + /* we own this one */ + gst_object_ref (GST_OBJECT (ret)); + gst_object_sink (GST_OBJECT (ret)); + + list = gst_tag_list_new (); + name = g_strdup_printf ("serial_%08lx", serialno); + + GST_RPAD_DIRECTION (ret) = GST_PAD_SRC; + ret->chain = chain; + ret->ogg = chain->ogg; + gst_object_set_name (GST_OBJECT (ret), name); + g_free (name); + + ret->serialno = serialno; + if (ogg_stream_init (&ret->stream, serialno) != 0) { + GST_ERROR ("Could not initialize ogg_stream struct for serial %08lx.", + serialno); + g_object_unref (G_OBJECT (ret)); + return NULL; + } + gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_SERIAL, serialno, + NULL); + //gst_element_found_tags (GST_ELEMENT (ogg), list); + gst_tag_list_free (list); + + GST_LOG ("created new ogg src %p for stream with serial %08lx", ret, + serialno); + + g_array_append_val (chain->streams, ret); + + return ret; +} + +static GstOggPad * +gst_ogg_chain_get_stream (GstOggChain * chain, glong serialno) +{ + gint i; + + for (i = 0; i < chain->streams->len; i++) { + GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i); + + if (pad->serialno == serialno) + return pad; + } + return NULL; +} + +static gboolean +gst_ogg_chain_has_stream (GstOggChain * chain, glong serialno) +{ + return gst_ogg_chain_get_stream (chain, serialno) != NULL; +} + +#define CURRENT_CHAIN(ogg) (&g_array_index ((ogg)->chains, GstOggChain, (ogg)->current_chain)) + /* signals and args */ enum { @@ -190,93 +875,34 @@ GST_STATIC_PAD_TEMPLATE ("sink", GST_STATIC_CAPS ("application/ogg") ); -/* different setup phases */ -typedef enum -{ - SETUP_INVALID, - SETUP_READ_FIRST_BOS, - SETUP_READ_BOS, - SETUP_FIND_LAST_CHAIN, - SETUP_FIND_END_OF_CHAIN, - SETUP_FIND_END_OF_STREAMS, - SETUP_FIND_END_OF_LAST_STREAMS -} -GstOggSetupState; - -typedef struct -{ - gboolean (*init) (GstOggDemux * ogg); - gboolean (*process) (GstOggDemux * ogg, ogg_page * page); -} -SetupStateFunc; - -static gboolean _read_bos_init (GstOggDemux * ogg); -static gboolean _read_bos_process (GstOggDemux * ogg, ogg_page * page); -static gboolean _find_chain_init (GstOggDemux * ogg); -static gboolean _find_chain_process (GstOggDemux * ogg, ogg_page * page); -static gboolean _find_last_chain_init (GstOggDemux * ogg); -static gboolean _find_last_chain_process (GstOggDemux * ogg, ogg_page * page); -static gboolean _find_streams_init (GstOggDemux * ogg); -static gboolean _find_streams_process (GstOggDemux * ogg, ogg_page * page); - -static SetupStateFunc setup_funcs[] = { - {NULL, NULL}, - {_read_bos_init, _read_bos_process}, - {_read_bos_init, _read_bos_process}, - {_find_last_chain_init, _find_last_chain_process}, - {_find_chain_init, _find_chain_process}, - {_find_streams_init, _find_streams_process}, - {_find_streams_init, _find_streams_process}, - {NULL, NULL} /* just because */ -}; - -static gboolean gst_ogg_demux_set_setup_state (GstOggDemux * ogg, - GstOggSetupState state); - static void gst_ogg_demux_finalize (GObject * object); -static gboolean gst_ogg_demux_src_event (GstPad * pad, GstEvent * event); -static const GstEventMask *gst_ogg_demux_get_event_masks (GstPad * pad); -static const GstQueryType *gst_ogg_demux_get_query_types (GstPad * pad); -static const GstFormat *gst_ogg_demux_get_formats (GstPad * pad); +//static const GstEventMask *gst_ogg_demux_get_event_masks (GstPad * pad); +//static const GstQueryType *gst_ogg_demux_get_query_types (GstPad * pad); +static GstOggChain *gst_ogg_demux_read_chain (GstOggDemux * ogg); +static gint gst_ogg_demux_read_end_chain (GstOggDemux * ogg, + GstOggChain * chain); -static gboolean gst_ogg_demux_src_query (GstPad * pad, - GstQueryType type, GstFormat * format, gint64 * value); - -static void gst_ogg_demux_iterate (GstFilePad * pad); static gboolean gst_ogg_demux_handle_event (GstPad * pad, GstEvent * event); - +static void gst_ogg_demux_loop (GstOggPad * pad); +static GstFlowReturn gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer); +static gboolean gst_ogg_demux_sink_activate (GstPad * sinkpad, + GstActivateMode mode); static GstElementStateReturn gst_ogg_demux_change_state (GstElement * element); -static GstOggPad *gst_ogg_pad_new (GstOggDemux * ogg, int serial_no); -static void gst_ogg_pad_remove (GstOggDemux * ogg, GstOggPad * ogg_pad); -static void gst_ogg_pad_reset (GstOggDemux * ogg, GstOggPad * pad); -static void gst_ogg_demux_push (GstOggDemux * ogg, ogg_page * page); -static void gst_ogg_pad_push (GstOggDemux * ogg, GstOggPad * ogg_pad); -static void gst_ogg_chains_clear (GstOggDemux * ogg); -static void gst_ogg_add_chain (GstOggDemux * ogg); - -static GstCaps *gst_ogg_type_find (ogg_packet * packet); - static void gst_ogg_print (GstOggDemux * demux); -#define GST_OGG_SET_STATE(ogg, new_state) G_STMT_START{ \ - GST_DEBUG_OBJECT (ogg, "setting state to %s", G_STRINGIFY (new_state)); \ - ogg->state = new_state; \ - ogg->setup_state = (new_state == GST_OGG_STATE_SETUP) ? \ - SETUP_READ_FIRST_BOS : SETUP_INVALID; \ -}G_STMT_END +GST_BOILERPLATE (GstOggDemux, gst_ogg_demux, GstElement, GST_TYPE_ELEMENT); -GST_BOILERPLATE (GstOggDemux, gst_ogg_demux, GstElement, GST_TYPE_ELEMENT) - - static void gst_ogg_demux_base_init (gpointer g_class) +static void +gst_ogg_demux_base_init (gpointer g_class) { GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); static GstElementDetails gst_ogg_demux_details = GST_ELEMENT_DETAILS ("ogg demuxer", "Codec/Demuxer", "demux ogg streams (info about ogg: http://xiph.org)", - "Benjamin Otte "); + "Wim Taymand "); gst_element_class_set_details (element_class, &gst_ogg_demux_details); @@ -299,22 +925,21 @@ gst_ogg_demux_class_init (GstOggDemuxClass * klass) static void gst_ogg_demux_init (GstOggDemux * ogg) { - GST_FLAG_SET (ogg, GST_ELEMENT_EVENT_AWARE); - /* create the sink pad */ ogg->sinkpad = - GST_FILE_PAD (gst_file_pad_new (gst_static_pad_template_get - (&ogg_demux_sink_template_factory), "sink")); - gst_file_pad_set_iterate_function (ogg->sinkpad, gst_ogg_demux_iterate); - gst_file_pad_set_event_function (ogg->sinkpad, gst_ogg_demux_handle_event); - gst_pad_set_formats_function (GST_PAD (ogg->sinkpad), - gst_ogg_demux_get_formats); - gst_element_add_pad (GST_ELEMENT (ogg), GST_PAD (ogg->sinkpad)); + gst_pad_new_from_template (gst_static_pad_template_get + (&ogg_demux_sink_template_factory), "sink"); + gst_pad_set_formats_function (ogg->sinkpad, gst_ogg_pad_formats); + gst_pad_set_loop_function (ogg->sinkpad, + (GstPadLoopFunction) gst_ogg_demux_loop); + gst_pad_set_event_function (ogg->sinkpad, gst_ogg_demux_handle_event); + gst_pad_set_chain_function (ogg->sinkpad, gst_ogg_demux_chain); + gst_pad_set_activate_function (ogg->sinkpad, gst_ogg_demux_sink_activate); + gst_element_add_pad (GST_ELEMENT (ogg), ogg->sinkpad); - /* initalize variables */ - GST_OGG_SET_STATE (ogg, GST_OGG_STATE_START); - ogg->chains = g_array_new (TRUE, TRUE, sizeof (GstOggChain)); - ogg->current_chain = -1; + ogg->chain_lock = g_mutex_new (); + ogg->chains = g_array_new (FALSE, TRUE, sizeof (GstOggChain *)); + ogg->state = OGG_STATE_NEW_CHAIN; } static void @@ -324,1331 +949,1063 @@ gst_ogg_demux_finalize (GObject * object) ogg = GST_OGG_DEMUX (object); + g_mutex_free (ogg->chain_lock); ogg_sync_clear (&ogg->sync); - /* chains are removed when going to READY */ - g_assert (ogg->current_chain == -1); - g_assert (ogg->chains->len == 0); - g_array_free (ogg->chains, TRUE); - if (G_OBJECT_CLASS (parent_class)->finalize) G_OBJECT_CLASS (parent_class)->finalize (object); } -static const GstFormat * -gst_ogg_demux_get_formats (GstPad * pad) -{ - static GstFormat src_formats[] = { - GST_FORMAT_BYTES, - GST_FORMAT_DEFAULT, /* granulepos */ - GST_FORMAT_TIME, - 0 - }; - static GstFormat sink_formats[] = { - GST_FORMAT_BYTES, - GST_FORMAT_DEFAULT, /* bytes */ - 0 - }; - - return (GST_PAD_IS_SRC (pad) ? src_formats : sink_formats); -} - -static const GstEventMask * -gst_ogg_demux_get_event_masks (GstPad * pad) -{ - static const GstEventMask gst_ogg_demux_src_event_masks[] = { - {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_FLUSH}, - {0,} - }; - - return gst_ogg_demux_src_event_masks; -} -static const GstQueryType * -gst_ogg_demux_get_query_types (GstPad * pad) -{ - static const GstQueryType gst_ogg_demux_src_query_types[] = { - GST_QUERY_TOTAL, - GST_QUERY_POSITION, - 0 - }; - - return gst_ogg_demux_src_query_types; -} - -static GstOggPad * -gst_ogg_get_pad_by_pad (GstOggDemux * ogg, GstPad * pad) -{ - GSList *walk; - GstOggPad *cur; - - if (ogg->current_chain == -1) { - GST_DEBUG_OBJECT (ogg, "no active chain, returning NULL"); - return NULL; - } - for (walk = CURRENT_CHAIN (ogg)->pads; walk; walk = g_slist_next (walk)) { - cur = (GstOggPad *) walk->data; - if (cur->pad == pad) - return cur; - } - return NULL; -} - -/* will subtract the base from a given granulepos in a stream - * (lineairly) and return the relative granulepos from the first - * packet in the stream, or some approximation thereof. Input is - * in granulepos units, output is either granulepos or time. - * Uses time internally. Returns -1 on error. - */ -static gint64 -get_relative (GstOggDemux * ogg, GstOggPad * cur, gint64 granpos, GstFormat out) -{ - gint64 time, start = -1, tmp; - GstFormat fmt; - - /* we're gonna ask our peer */ - if (!cur->pad || !GST_PAD_PEER (cur->pad)) - return -1; - - /* lineair unit (time) */ - fmt = GST_FORMAT_TIME; - if (!gst_pad_convert (GST_PAD_PEER (cur->pad), - GST_FORMAT_DEFAULT, granpos, &fmt, &time)) - return -1; - - /* get base for this chain */ - FOR_PAD_IN_CURRENT_CHAIN (ogg, pad, - if (pad->start != -1 && pad->pad && - GST_PAD_PEER (pad->pad) && - gst_pad_convert (GST_PAD_PEER (pad->pad), - GST_FORMAT_DEFAULT, pad->start, - &fmt, &tmp) && (start == -1 || tmp < start)) - start = tmp;); - if (start == -1) - return -1; - - /* base is *end of first page*, so subtract $random amount to make - * us think it's the start of the page (= 1 second) */ - if (start > GST_SECOND) - start -= GST_SECOND; - else - start = 0; - - /* subtract */ - if (time > start) - time -= start; - else - time = 0; - - /* convert back to $outputformat */ - if (!gst_pad_convert (GST_PAD_PEER (cur->pad), - GST_FORMAT_TIME, time, &out, &tmp)) - return -1; - - return tmp; -} - -/* the query function on the src pad only knows about granulepos - * values but we can use the peer plugins to convert the granulepos - * (which is supposed to be the default format) to any other format - */ -static gboolean -gst_ogg_demux_src_query (GstPad * pad, GstQueryType type, - GstFormat * format, gint64 * value) -{ - gboolean res = FALSE; - GstOggDemux *ogg; - GstOggPad *cur; - guint64 granulepos = 0; - - ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad)); - - cur = gst_ogg_get_pad_by_pad (ogg, pad); - if (!cur) - return FALSE; - - switch (type) { - case GST_QUERY_TOTAL:{ - if (cur->length != 0 && cur->length > cur->start) { - granulepos = cur->length; - res = TRUE; - } - break; - } - case GST_QUERY_POSITION: - if (cur->length != 0 && cur->length > cur->start) { - granulepos = cur->known_offset; - res = TRUE; - } - break; - default: - break; - } - - if (res) { - gint64 time; - - time = get_relative (ogg, cur, granulepos, GST_FORMAT_TIME); - if (time == -1) - return FALSE; - - /* still ok, got a granulepos then */ - switch (*format) { - case GST_FORMAT_TIME: - /* fine, result should be granulepos */ - *value = time; - break; - default: - /* something we have to ask our peer */ - if (GST_PAD_PEER (pad)) { - res = gst_pad_convert (GST_PAD_PEER (pad), - GST_FORMAT_TIME, time, format, value); - } else { - res = FALSE; - } - break; - } - } - return res; -} - -/* The current seeking implementation is the most simple I could come up with: - * - when seeking forwards, just discard data until desired position is reached - * - when seeking backwards, seek to beginning and seek forward from there - * Anyone is free to improve this algorithm as it is quite stupid and probably - * really slow. - * - * The seeking position can be specified as the granulepos in case a decoder - * plugin can give us a correct granulepos, or in timestamps. - * In the case of a time seek, we repeadedly ask the peer element to - * convert the granulepos in the page to a timestamp. We go back to playing - * when the timestamp is the requested one (or close enough to it). - */ -static gboolean -gst_ogg_demux_src_event (GstPad * pad, GstEvent * event) -{ - GstOggDemux *ogg; - GstOggPad *cur; - - ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad)); - cur = gst_ogg_get_pad_by_pad (ogg, pad); - - /* FIXME: optimize this so events from inactive chains work? - * in theory there shouldn't be an exisiting pad for inactive chains */ - if (cur == NULL) - goto error; - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEEK: - { - gint64 offset, position, total, seek_offset; - GstFormat format, my_format; - gboolean res; - - format = GST_EVENT_SEEK_FORMAT (event); - offset = GST_EVENT_SEEK_OFFSET (event); - - my_format = format; - - /* get position, we'll need it later to decide what direction - * we need to seek in */ - res = gst_ogg_demux_src_query (pad, - GST_QUERY_POSITION, &my_format, &position); - if (!res) - goto error; - - switch (GST_EVENT_SEEK_METHOD (event)) { - case GST_SEEK_METHOD_END: - { - gint64 value; - - /* invalid offset */ - if (offset > 0) - goto error; - - /* calculate total length first */ - res = gst_ogg_demux_src_query (pad, - GST_QUERY_TOTAL, &my_format, &value); - if (!res) - goto error; - - /* requested position is end + offset */ - offset = value + offset; - break; - } - case GST_SEEK_METHOD_CUR: - { - /* add current position to offset */ - offset = position + offset; - break; - } - case GST_SEEK_METHOD_SET: - /* offset and format are fine here */ - break; - default: - g_warning ("invalid seek method in seek event"); - goto error; - } - - my_format = GST_FORMAT_TIME; - if (format != GST_FORMAT_TIME) { - if (!GST_PAD_PEER (pad) || - !gst_pad_convert (GST_PAD_PEER (pad), format, - offset, &my_format, &position)) - goto error; - } else { - position = offset; - } - if (!gst_ogg_demux_src_query (pad, GST_QUERY_TOTAL, &my_format, &total)) - goto error; - if (position < 0) - position = 0; - else if (position > total) - position = total; - seek_offset = gst_file_pad_get_length (ogg->sinkpad) * - ((gdouble) position) / ((gdouble) total); - if (gst_file_pad_seek (ogg->sinkpad, seek_offset, - GST_SEEK_METHOD_SET) != 0) - goto error; - ogg->seek_try = 1; - ogg_sync_clear (&ogg->sync); - - GST_OGG_SET_STATE (ogg, GST_OGG_STATE_SEEK); - FOR_PAD_IN_CURRENT_CHAIN (ogg, pad, - pad->flags |= GST_OGG_PAD_NEEDS_DISCONT;); - if (GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH) { - FOR_PAD_IN_CURRENT_CHAIN (ogg, pad, - pad->flags |= GST_OGG_PAD_NEEDS_FLUSH;); - } - GST_DEBUG_OBJECT (ogg, - "initiating seeking to format %d, offset %" G_GUINT64_FORMAT, format, - offset); - - /* store format and position we seek to */ - ogg->seek_pad = cur; - ogg->seek_to = position; - ogg->seek_format = GST_FORMAT_TIME; - ogg->seek_offset = seek_offset; - - gst_event_unref (event); - return TRUE; - } - default: - return gst_pad_event_default (pad, event); - } - - g_assert_not_reached (); - -error: - gst_event_unref (event); - return FALSE; -} - -static gboolean -gst_ogg_demux_src_convert (GstPad * pad, - GstFormat src_format, gint64 src_value, - GstFormat * dest_format, gint64 * dest_value) -{ - gboolean res = FALSE; - GstOggDemux *ogg; - GstOggPad *cur; - - ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad)); - cur = gst_ogg_get_pad_by_pad (ogg, pad); - - /* fill me, not sure with what... */ - - return res; -} - -static void -gst_ogg_start_playing (GstOggDemux * ogg) -{ - GST_DEBUG_OBJECT (ogg, "done with setup, changing to playback now"); - if (gst_file_pad_seek (ogg->sinkpad, 0, GST_SEEK_METHOD_SET) != 0) { - GST_ELEMENT_ERROR (ogg, CORE, SEEK, (NULL), - ("cannot seek to start after EOS")); - } - ogg_sync_clear (&ogg->sync); - if (ogg->current_chain >= 0) { - ogg->current_chain = 0; - } else { - gst_ogg_add_chain (ogg); - } - GST_FLAG_UNSET (ogg, GST_OGG_FLAG_EOS); - GST_FLAG_SET (ogg, GST_OGG_FLAG_WAIT_FOR_DISCONT); - GST_OGG_SET_STATE (ogg, GST_OGG_STATE_PLAY); - gst_ogg_print (ogg); -} - static gboolean gst_ogg_demux_handle_event (GstPad * pad, GstEvent * event) { - GstOggDemux *ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad)); + GstOggDemux *ogg = GST_OGG_DEMUX (GST_PAD_PARENT (pad)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_DISCONTINUOUS: GST_DEBUG_OBJECT (ogg, "got a discont event"); ogg_sync_reset (&ogg->sync); gst_event_unref (event); - GST_FLAG_UNSET (ogg, GST_OGG_FLAG_WAIT_FOR_DISCONT); - FOR_PAD_IN_CURRENT_CHAIN (ogg, pad, - pad->flags |= GST_OGG_PAD_NEEDS_DISCONT;); break; default: - gst_pad_event_default (pad, event); - break; + return gst_pad_event_default (pad, event); } return TRUE; } -static void -gst_ogg_demux_eos (GstOggDemux * ogg) +/* submit the given buffer to the ogg sync. + * + * Returns the number of bytes submited. + */ +static gint +gst_ogg_demux_submit_buffer (GstOggDemux * ogg, GstBuffer * buffer) { - guint i; - GSList *walk; - GstEvent *event; + guint size; + guint8 *data; + gchar *oggbuffer; - GST_DEBUG_OBJECT (ogg, "got EOS"); - ogg->current_chain = -1; - if (ogg->state == GST_OGG_STATE_SETUP) { - gst_ogg_start_playing (ogg); - return; + size = GST_BUFFER_SIZE (buffer); + data = GST_BUFFER_DATA (buffer); + + oggbuffer = ogg_sync_buffer (&ogg->sync, size); + memcpy (oggbuffer, data, size); + ogg_sync_wrote (&ogg->sync, size); + + return size; +} + +/* in random access mode this code updates the current read position + * and resets the ogg sync buffer so that the next read will happen + * from this new location. + */ +static void +gst_ogg_demux_seek (GstOggDemux * ogg, gint64 offset) +{ + GST_LOG_OBJECT (ogg, "seeking to %lld", offset); + + ogg->offset = offset; + ogg_sync_reset (&ogg->sync); +} + +/* read more data from the current offset and submit to + * the ogg sync layer. + * + * Return number of bytes written. + */ +static gint +gst_ogg_demux_get_data (GstOggDemux * ogg) +{ + GstFlowReturn ret; + GstBuffer *buffer; + gint size; + + GST_LOG_OBJECT (ogg, "get data %lld", ogg->offset); + if (ogg->offset == ogg->length) + return 0; + + ret = gst_pad_pull_range (ogg->sinkpad, ogg->offset, CHUNKSIZE, &buffer); + if (ret != GST_FLOW_OK) + return -1; + + size = gst_ogg_demux_submit_buffer (ogg, buffer); + gst_buffer_unref (buffer); + + return size; +} + +/* Read the next page from the current offset. + */ +static gint64 +gst_ogg_demux_get_next_page (GstOggDemux * ogg, ogg_page * og, gint64 boundary) +{ + gint64 end_offset = 0; + + GST_LOG_OBJECT (ogg, "get next page %lld", boundary); + + if (boundary > 0) + end_offset = ogg->offset + boundary; + + while (TRUE) { + glong more; + + if (boundary > 0 && ogg->offset >= end_offset) { + GST_LOG_OBJECT (ogg, "offset %lld >= end_offset %lld", ogg->offset, + end_offset); + return OV_FALSE; + } + + more = ogg_sync_pageseek (&ogg->sync, og); + + if (more < 0) { + GST_LOG_OBJECT (ogg, "skipped %ld bytes", more); + /* skipped n bytes */ + ogg->offset -= more; + } else if (more == 0) { + gint ret; + + /* send more paramedics */ + if (boundary == 0) + return OV_FALSE; + + ret = gst_ogg_demux_get_data (ogg); + if (ret == 0) + return OV_EOF; + if (ret < 0) + return OV_EREAD; + } else { + /* got a page. Return the offset at the page beginning, + advance the internal offset past the page end */ + gint64 ret = ogg->offset; + + ogg->offset += more; + /* need to reset as we do not keep track of the bytes we + * sent to the sync layer */ + ogg_sync_reset (&ogg->sync); + + GST_LOG_OBJECT (ogg, + "got page at %lld, serial %08lx, end at %lld, granule %lld", ret, + ogg_page_serialno (og), ogg->offset, ogg_page_granulepos (og)); + + return ret; + } } - event = gst_event_new (GST_EVENT_EOS); - for (i = 0; i < ogg->chains->len; i++) { - GstOggChain *chain = &g_array_index (ogg->chains, GstOggChain, i); +} - for (walk = chain->pads; walk; walk = g_slist_next (walk)) { - GstOggPad *pad = (GstOggPad *) walk->data; +/* from the current offset, find the previous page, seeking backwards + * until we find the page. */ +static gint +gst_ogg_demux_get_prev_page (GstOggDemux * ogg, ogg_page * og) +{ + gint64 begin = ogg->offset; + gint64 end = begin; + gint64 ret; + gint64 offset = -1; - if (pad->pad && GST_PAD_IS_USABLE (pad->pad)) { - gst_data_ref (GST_DATA (event)); - gst_pad_push (pad->pad, GST_DATA (event)); + while (offset == -1) { + begin -= CHUNKSIZE; + if (begin < 0) + begin = 0; + + gst_ogg_demux_seek (ogg, begin); + + /* now continue reading until we run out of data, if we find a page + * start, we save it. It might not be the final page as there could be + * another page after this one. */ + while (ogg->offset < end) { + ret = gst_ogg_demux_get_next_page (ogg, og, end - ogg->offset); + if (ret == OV_EREAD) + return OV_EREAD; + if (ret < 0) { + break; + } else { + offset = ret; } } } - gst_element_set_eos (GST_ELEMENT (ogg)); - gst_event_unref (event); + + /* we have the offset. Actually snork and hold the page now */ + gst_ogg_demux_seek (ogg, offset); + ret = gst_ogg_demux_get_next_page (ogg, og, CHUNKSIZE); + if (ret < 0) + /* this shouldn't be possible */ + return OV_EFAULT; + + return offset; } -static GstOggPad * -gst_ogg_pad_get_in_chain (GstOggDemux * ogg, guint chain, int serial) +static gboolean +gst_ogg_demux_deactivate_current_chain (GstOggDemux * ogg) { - FOR_PAD_IN_CHAIN (ogg, pad, chain, if (pad->serial == serial) - return pad;); + gint i; + GstOggChain *chain = ogg->current_chain; + + if (chain == NULL) + return TRUE; + + for (i = 0; i < chain->streams->len; i++) { + GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i); + + gst_element_remove_pad (GST_ELEMENT (ogg), GST_PAD (pad)); + } + /* if we cannot seek, we can destroy the chain completely */ + if (!ogg->seekable) { + gst_ogg_chain_free (chain); + } + ogg->current_chain = NULL; + + return TRUE; +} + +static gboolean +gst_ogg_demux_activate_chain (GstOggDemux * ogg, GstOggChain * chain) +{ + gint i; + + if (chain == ogg->current_chain) + return TRUE; + + gst_ogg_demux_deactivate_current_chain (ogg); + + for (i = 0; i < chain->streams->len; i++) { + GstOggPad *pad; + GList *headers; + + pad = g_array_index (chain->streams, GstOggPad *, i); + + gst_element_add_pad (GST_ELEMENT (ogg), GST_PAD (pad)); + + for (headers = pad->headers; headers; headers = g_list_next (headers)) { + GstBuffer *buffer = GST_BUFFER (headers->data); + + gst_pad_push (GST_PAD_CAST (pad), buffer); + } + } + + ogg->current_chain = chain; + + return TRUE; +} + +static gboolean +gst_ogg_demux_perform_seek (GstOggDemux * ogg, gint64 pos) +{ + GstOggChain *chain = NULL; + gint64 begin, end; + gint64 begintime, endtime; + gint64 target; + gint64 best; + gint64 total; + gint64 result = 0; + gint i; + + total = ogg->total_time; + + /* can't seek past start or end */ + if (pos < 0 || pos > total) { + return FALSE; + } + + /* first step is to unlock the streaming thread if it is + * blocked in a chain call, we do this by starting the flush. because + * we cannot yet hold any streaming lock, we have to protect the chains + * with their own lock. */ + { + gint i; + + GST_CHAIN_LOCK (ogg); + for (i = 0; i < ogg->chains->len; i++) { + GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i); + gint j; + + for (j = 0; j < chain->streams->len; j++) { + GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, j); + + gst_pad_push_event (GST_PAD (pad), gst_event_new_flush (FALSE)); + } + } + GST_CHAIN_UNLOCK (ogg); + } + + /* now grab the stream lock so that streaming cannot continue */ + GST_STREAM_LOCK (ogg->sinkpad); + + { + gint i; + + /* reset all ogg streams now, need to do this from within the lock to + * make sure the streaming thread is not messing with the stream */ + for (i = 0; i < ogg->chains->len; i++) { + GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i); + gint j; + + for (j = 0; j < chain->streams->len; j++) { + GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, j); + + ogg_stream_reset (&pad->stream); + } + } + } + + /* first find the chain to search in */ + for (i = ogg->chains->len - 1; i >= 0; i--) { + chain = g_array_index (ogg->chains, GstOggChain *, i); + total -= chain->total_time; + if (pos >= total) + break; + } + + begin = chain->offset; + end = chain->end_offset; + begintime = chain->begin_time; + endtime = chain->begin_time + chain->total_time; + target = pos - total + begintime; + best = begin; + + GST_DEBUG_OBJECT (ogg, "seeking to %" GST_TIME_FORMAT " in chain %p", + GST_TIME_ARGS (pos), chain); + GST_DEBUG_OBJECT (ogg, + "chain offset %" G_GINT64_FORMAT ", end offset %" G_GINT64_FORMAT, begin, + end); + GST_DEBUG_OBJECT (ogg, + "chain begin time %" GST_TIME_FORMAT ", end time %" GST_TIME_FORMAT, + GST_TIME_ARGS (begintime), GST_TIME_ARGS (endtime)); + GST_DEBUG_OBJECT (ogg, "target %" GST_TIME_FORMAT, GST_TIME_ARGS (target)); + + /* perform the seek */ + while (begin < end) { + gint64 bisect; + + if ((end - begin < CHUNKSIZE) || (endtime == begintime)) { + bisect = begin; + } else { + /* take a (pretty decent) guess, avoiding overflow */ + gint64 rate = (end - begin) * GST_MSECOND / (endtime - begintime); + + bisect = (target - begintime) / GST_MSECOND * rate + begin - CHUNKSIZE; + + if (bisect <= begin) + bisect = begin + 1; + } + gst_ogg_demux_seek (ogg, bisect); + + while (begin < end) { + ogg_page og; + + GST_DEBUG_OBJECT (ogg, + "after seek, bisect %" G_GINT64_FORMAT ", begin %" G_GINT64_FORMAT + ", end %" G_GINT64_FORMAT, bisect, begin, end); + + result = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset); + if (result == OV_EREAD) { + goto seek_error; + } + + if (result < 0) { + if (bisect <= begin + 1) { + end = begin; /* found it */ + } else { + if (bisect == 0) + goto seek_error; + + bisect -= CHUNKSIZE; + if (bisect <= begin) + bisect = begin + 1; + + gst_ogg_demux_seek (ogg, bisect); + } + } else { + gint64 granulepos; + GstClockTime granuletime; + GstFormat format = GST_FORMAT_TIME; + GstOggPad *pad; + + granulepos = ogg_page_granulepos (&og); + if (granulepos == -1) + continue; + + pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og)); + if (pad == NULL) + continue; + + gst_pad_convert (pad->elem_pad, + GST_FORMAT_DEFAULT, granulepos, &format, &granuletime); + + GST_DEBUG_OBJECT (ogg, + "found page with granule %" G_GINT64_FORMAT " and time %" + GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime)); + + if (granuletime < target) { + best = result; /* raw offset of packet with granulepos */ + begin = ogg->offset; /* raw offset of next page */ + begintime = granuletime; + + if (target - begintime > GST_SECOND) + break; + + bisect = begin; /* *not* begin + 1 */ + } else { + if (bisect <= begin + 1) { + end = begin; /* found it */ + } else { + if (end == ogg->offset) { /* we're pretty close - we'd be stuck in */ + end = result; + bisect -= CHUNKSIZE; /* an endless loop otherwise. */ + if (bisect <= begin) + bisect = begin + 1; + gst_ogg_demux_seek (ogg, bisect); + } else { + end = result; + endtime = granuletime; + break; + } + } + } + } + } + } + + ogg->offset = best; + + /* now we have a new position, prepare for streaming again */ + { + gint i; + GstEvent *event; + + /* create the discont event we are going to send out */ + event = gst_event_new_discontinuous (1.0, + GST_FORMAT_TIME, (gint64) pos, (gint64) ogg->total_time, NULL); + + for (i = 0; i < ogg->chains->len; i++) { + GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i); + gint j; + + for (j = 0; j < chain->streams->len; j++) { + GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, j); + + gst_event_ref (event); + /* queue the event for the streaming thread */ + pad->new_segment = event; + gst_pad_push_event (GST_PAD (pad), gst_event_new_flush (TRUE)); + } + } + gst_event_unref (event); + /* restart our task since it might have been stopped when we did the + * flush. */ + gst_task_start (GST_RPAD_TASK (ogg->sinkpad)); + } + + /* switch to different chain */ + if (chain != ogg->current_chain) { + gst_ogg_demux_activate_chain (ogg, chain); + } + + /* streaming can continue now */ + GST_STREAM_UNLOCK (ogg->sinkpad); + + return TRUE; + +seek_error: + GST_DEBUG_OBJECT (ogg, "got a seek error"); + GST_STREAM_UNLOCK (ogg->sinkpad); + + return FALSE; +} + +/* finds each bitstream link one at a time using a bisection search + * (has to begin by knowing the offset of the lb's initial page). + * Recurses for each link so it can alloc the link storage after + * finding them all, then unroll and fill the cache at the same time + */ +static gint +gst_ogg_demux_bisect_forward_serialno (GstOggDemux * ogg, + gint64 begin, gint64 searched, gint64 end, GstOggChain * chain, glong m) +{ + gint64 endsearched = end; + gint64 next = end; + ogg_page og; + gint64 ret; + GstOggChain *nextchain; + + GST_LOG_OBJECT (ogg, + "bisect begin: %lld, searched: %lld, end %lld, chain: %p", begin, + searched, end, chain); + + /* the below guards against garbage seperating the last and + * first pages of two links. */ + while (searched < endsearched) { + gint64 bisect; + + if (endsearched - searched < CHUNKSIZE) { + bisect = searched; + } else { + bisect = (searched + endsearched) / 2; + } + + gst_ogg_demux_seek (ogg, bisect); + ret = gst_ogg_demux_get_next_page (ogg, &og, -1); + if (ret == OV_EREAD) { + GST_LOG_OBJECT (ogg, "OV_READ"); + return OV_EREAD; + } + + if (ret < 0) { + endsearched = bisect; + } else { + glong serial = ogg_page_serialno (&og); + + if (!gst_ogg_chain_has_stream (chain, serial)) { + endsearched = bisect; + next = ret; + } else { + searched = ret + og.header_len + og.body_len; + } + } + } + + GST_LOG_OBJECT (ogg, "current chain ends at %lld", searched); + + chain->end_offset = searched; + gst_ogg_demux_read_end_chain (ogg, chain); + + GST_LOG_OBJECT (ogg, "found begin at %lld", next); + + gst_ogg_demux_seek (ogg, next); + nextchain = gst_ogg_demux_read_chain (ogg); + + if (searched < end && nextchain != NULL) { + ret = gst_ogg_demux_bisect_forward_serialno (ogg, next, ogg->offset, + end, nextchain, m + 1); + + if (ret == OV_EREAD) { + GST_LOG_OBJECT (ogg, "OV_READ"); + return OV_EREAD; + } + } + g_array_insert_val (ogg->chains, 0, chain); + + return 0; +} + +/* read a chain from the ogg file. This code will + * read all BOS pages and will create and return a GstOggChain + * structure with the results. + * + * This function will also read N pages from each stream in the + * chain and submit them to the decoders. When the decoder has + * decoded the first buffer, we know the timestamp of the first + * page in the chain. + */ +static GstOggChain * +gst_ogg_demux_read_chain (GstOggDemux * ogg) +{ + GstOggChain *chain = NULL; + gint64 offset = ogg->offset; + ogg_page op; + gboolean done; + gint i; + + GST_LOG_OBJECT (ogg, "reading chain at %lld", offset); + + /* first read the BOS pages, do typefind on them, create + * the decoders, send data to the decoders. */ + while (TRUE) { + GstOggPad *pad; + glong serial; + gint ret; + + ret = gst_ogg_demux_get_next_page (ogg, &op, -1); + if (ret < 0 || !ogg_page_bos (&op)) + break; + + if (chain == NULL) { + chain = gst_ogg_chain_new (ogg); + chain->offset = offset; + } + + serial = ogg_page_serialno (&op); + pad = gst_ogg_chain_new_stream (chain, serial); + gst_ogg_pad_submit_page (pad, &op); + } + if (chain == NULL) { + GST_WARNING_OBJECT (ogg, "failed to read chain"); + return NULL; + } + chain->have_bos = TRUE; + GST_LOG_OBJECT (ogg, "read bos pages, init decoder now"); + + /* now read pages until we receive a buffer from each of the + * stream decoders, this will tell us the timestamp of the + * first packet in the chain then */ + done = FALSE; + while (!done) { + glong serial; + gint ret; + + serial = ogg_page_serialno (&op); + done = TRUE; + for (i = 0; i < chain->streams->len; i++) { + GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i); + + if (pad->serialno == serial) { + gst_ogg_pad_submit_page (pad, &op); + } + /* the timestamp will be filled in when we submit the pages */ + done &= (pad->start_time != -1); + GST_LOG_OBJECT (ogg, "done %08lx now %d", serial, done); + } + + if (!done) { + ret = gst_ogg_demux_get_next_page (ogg, &op, -1); + if (ret < 0) + break; + } + } + GST_LOG_OBJECT (ogg, "done reading chain"); + /* now we can fill in the missing info using queries */ + for (i = 0; i < chain->streams->len; i++) { + GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i); + GstFormat target = GST_FORMAT_TIME; + + gst_pad_convert (pad->elem_pad, + GST_FORMAT_DEFAULT, pad->first_granule, &target, &pad->first_time); + + pad->mode = GST_OGG_PAD_MODE_STREAMING; + pad->packetno = 0; + } + return chain; +} + +/* read the last pages from the ogg stream to get the final + * page end_offsets. + */ +static gint +gst_ogg_demux_read_end_chain (GstOggDemux * ogg, GstOggChain * chain) +{ + gint64 begin = chain->end_offset; + gint64 end = begin; + gint64 ret; + gboolean done = FALSE; + ogg_page og; + gint i; + + while (!done) { + begin -= CHUNKSIZE; + if (begin < 0) + begin = 0; + + gst_ogg_demux_seek (ogg, begin); + + /* now continue reading until we run out of data, if we find a page + * start, we save it. It might not be the final page as there could be + * another page after this one. */ + while (ogg->offset < end) { + ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset); + if (ret == OV_EREAD) + return OV_EREAD; + if (ret < 0) { + break; + } else { + done = TRUE; + for (i = 0; i < chain->streams->len; i++) { + GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i); + + if (pad->serialno == ogg_page_serialno (&og)) { + gint64 granulepos = ogg_page_granulepos (&og); + + if (pad->last_granule < granulepos) { + pad->last_granule = granulepos; + } + } else { + done &= (pad->last_granule != -1); + } + } + } + } + } + /* now we can fill in the missing info using queries */ + for (i = 0; i < chain->streams->len; i++) { + GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i); + GstFormat target = GST_FORMAT_TIME; + + gst_pad_convert (pad->elem_pad, + GST_FORMAT_DEFAULT, pad->last_granule, &target, &pad->last_time); + } + return 0; +} + +/* find a pad with a given serial number + */ +static GstOggPad * +gst_ogg_demux_find_pad (GstOggDemux * ogg, int serialno) +{ + GstOggPad *pad; + gint i; + + /* first look in current chain if any */ + if (ogg->current_chain) { + pad = gst_ogg_chain_get_stream (ogg->current_chain, serialno); + if (pad) + return pad; + } + + for (i = 0; i < ogg->chains->len; i++) { + GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i); + + pad = gst_ogg_chain_get_stream (chain, serialno); + if (pad) + return pad; + } return NULL; } -/* get the pad with the given serial in the current stream or NULL if none */ -static GstOggPad * -gst_ogg_pad_get_in_current_chain (GstOggDemux * ogg, int serial) +/* find a chain with a given serial number + */ +static GstOggChain * +gst_ogg_demux_find_chain (GstOggDemux * ogg, int serialno) { - if (ogg->current_chain == -1) - return NULL; - g_return_val_if_fail (ogg->current_chain < ogg->chains->len, NULL); - return gst_ogg_pad_get_in_chain (ogg, ogg->current_chain, serial); -} - -/* FIXME: HACK - i dunno if this is supported ogg API */ -static guint -gst_ogg_page_get_length (ogg_page * page) -{ - return page->header_len + page->body_len; -} - -static gint64 -gst_ogg_demux_position (GstOggDemux * ogg) -{ - gint64 pos = gst_file_pad_tell (ogg->sinkpad); - - if (pos < 0) - return pos; - - return pos - ogg->sync.fill + ogg->sync.returned; -} - -/* END HACK */ - -/* fill in values from this page */ -#include -static void -gst_ogg_pad_populate (GstOggDemux * ogg, GstOggPad * pad, ogg_page * page) -{ - gint64 start, end, granpos = ogg_page_granulepos (page); - - if (pad->start > granpos && granpos > 0) - pad->start = granpos; - if (pad->length < granpos && granpos > 0) - pad->length = granpos; - if (pad->pages < ogg_page_pageno (page)) - pad->pages = ogg_page_pageno (page); - end = gst_ogg_demux_position (ogg); - if (end >= 0) { - /* we need to know the offsets into the stream for the current page */ - start = end - gst_ogg_page_get_length (page); - //g_print ("really setting start from %lld to %lld\n", pad->start_offset, start); - //g_print ("really setting end from %lld to %lld\n", pad->end_offset, end); - if (start < pad->start_offset || pad->start_offset < 0) - pad->start_offset = start; - if (ogg_page_bos (page)) - pad->start_found = TRUE; - if (end > pad->end_offset) - pad->end_offset = end; - if (ogg_page_eos (page)) - pad->end_found = TRUE; - } -} - -/* get the ogg pad with the given serial in the unordered list or create and add it */ -static GstOggPad * -gst_ogg_pad_get_unordered (GstOggDemux * ogg, ogg_page * page) -{ - GSList *walk; GstOggPad *pad; - int serial = ogg_page_serialno (page); - for (walk = ogg->unordered; walk; walk = g_slist_next (walk)) { - pad = (GstOggPad *) walk->data; - - if (pad->serial == serial) - goto out; - } - pad = gst_ogg_pad_new (ogg, serial); - ogg->unordered = g_slist_prepend (ogg->unordered, pad); - -out: - /* update start and end pointer if applicable */ - gst_ogg_pad_populate (ogg, pad, page); - - return pad; -} - -static GstOggPad * -gst_ogg_pad_get (GstOggDemux * ogg, ogg_page * page) -{ - GstOggPad *pad = - gst_ogg_pad_get_in_current_chain (ogg, ogg_page_serialno (page)); + pad = gst_ogg_demux_find_pad (ogg, serialno); if (pad) { - gst_ogg_pad_populate (ogg, pad, page); - } else { - pad = gst_ogg_pad_get_unordered (ogg, page); + return pad->chain; } - return pad; + return NULL; } -static void -gst_ogg_add_chain (GstOggDemux * ogg) -{ - GST_LOG_OBJECT (ogg, "adding chain %u", ogg->chains->len); - ogg->current_chain = ogg->chains->len; - g_array_set_size (ogg->chains, ogg->chains->len + 1); -} - -/* abort setup phase and just start playing */ -static void -abort_setup (GstOggDemux * ogg) -{ - gst_ogg_print (ogg); - gst_ogg_chains_clear (ogg); - gst_ogg_start_playing (ogg); -} - -#undef GST_CAT_DEFAULT -#define GST_CAT_DEFAULT gst_ogg_demux_setup_debug -static gboolean -gst_ogg_demux_set_setup_state (GstOggDemux * ogg, GstOggSetupState state) -{ - g_assert (ogg->state == GST_OGG_STATE_SETUP); - g_assert (state > 0); - g_assert (state < G_N_ELEMENTS (setup_funcs)); - g_assert (state != ogg->setup_state); - - GST_DEBUG_OBJECT (ogg, "setting setup state from %d to %d", ogg->setup_state, - state); - ogg->setup_state = state; - if (!setup_funcs[state].init (ogg)) { - abort_setup (ogg); - return FALSE; - } - - return TRUE; -} - -/* seeks to the given position if TRUE is returned. Seeks a bit before this - * offset for syncing. You can call this function multiple times, if sync - * failed, it will then seek further back. It will never seek further back as - * min_offset though. +/* find all the chains in the ogg file, this reads the first and + * last page of the ogg stream, if they match then the ogg file has + * just one chain, else we do a binary search for all chains. */ static gboolean -gst_ogg_demux_seek_before (GstOggDemux * ogg, gint64 offset, gint64 min_offset) +gst_ogg_demux_find_chains (GstOggDemux * ogg) { - gint64 before; + ogg_page og; + GstPad *peer; + GstFormat format; + gboolean res; + gulong serialno; GstOggChain *chain; - gint streams; - /* figure out how many streams are in this chain */ - chain = CURRENT_CHAIN (ogg); - if (chain) { - streams = g_slist_length (chain->pads); + /* get peer to figure out length */ + if ((peer = gst_pad_get_peer (ogg->sinkpad)) == NULL) + goto no_peer; + + /* find length to read last page, we store this for later use. */ + format = GST_FORMAT_BYTES; + res = gst_pad_query (peer, GST_QUERY_TOTAL, &format, &ogg->length); + gst_object_unref (GST_OBJECT (peer)); + if (!res) + goto no_length; + + /* read chain from offset 0, this is the first chain of the + * ogg file. */ + gst_ogg_demux_seek (ogg, 0); + chain = gst_ogg_demux_read_chain (ogg); + if (chain == NULL) + goto no_first_chain; + + /* read page from end offset, we use this page to check if its serial + * number is contained in the first chain. If this is the case then + * this ogg is not a chained ogg and we can skip the scanning. */ + gst_ogg_demux_seek (ogg, ogg->length); + gst_ogg_demux_get_prev_page (ogg, &og); + serialno = ogg_page_serialno (&og); + + if (!gst_ogg_chain_has_stream (chain, serialno)) { + /* the last page is not in the first stream, this means we should + * find all the chains in this chained ogg. */ + gst_ogg_demux_bisect_forward_serialno (ogg, 0, 0, ogg->length, chain, 0); } else { - streams = 1; + /* we still call this function here but with an empty range so that + * we can reuse the setup code in this routine. */ + gst_ogg_demux_bisect_forward_serialno (ogg, 0, ogg->length, ogg->length, + chain, 0); } + /* collect all info */ + { + gint i, j; - /* need to multiply the expected page size with the numer of streams we - * detected to have a good chance of finding all pages */ - before = ogg->seek_skipped ? ogg->seek_skipped * SETUP_SEEK_MULTIPLIER : - SETUP_EXPECTED_PAGE_SIZE * streams; + ogg->total_time = 0; - GST_DEBUG_OBJECT (ogg, - "seeking to %" G_GINT64_FORMAT " bytes before %" G_GINT64_FORMAT, - before, offset); - /* tried to seek to start once, don't try again */ - if (min_offset + ogg->seek_skipped > offset) - return FALSE; - if (gst_file_pad_seek (ogg->sinkpad, MAX (min_offset, offset - before), - GST_SEEK_METHOD_SET) != 0) - return FALSE; - ogg_sync_clear (&ogg->sync); - ogg->seek_skipped = before; - ogg->seek_to = offset; + for (i = 0; i < ogg->chains->len; i++) { + GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i); - return TRUE; -} + chain->total_time = 0; + chain->start_time = 0; + chain->last_time = 0; + chain->begin_time = ogg->total_time; -static gboolean -_read_bos_init (GstOggDemux * ogg) -{ - gst_ogg_add_chain (ogg); + for (j = 0; j < chain->streams->len; j++) { + GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, j); - return TRUE; -} - -static gboolean -_read_bos_process (GstOggDemux * ogg, ogg_page * page) -{ - /* here we're reading in the bos pages of the current chain */ - if (ogg_page_bos (page)) { - GstOggPad *pad; - - GST_LOG_OBJECT (ogg, - "SETUP_READ_BOS: bos found with serial %d, adding to current chain", - ogg_page_serialno (page)); - pad = gst_ogg_pad_get_unordered (ogg, page); - ogg->unordered = g_slist_remove (ogg->unordered, pad); - g_assert (CURRENT_CHAIN (ogg)); - CURRENT_CHAIN (ogg)->pads = - g_slist_prepend (CURRENT_CHAIN (ogg)->pads, pad); - } else { - gboolean have_all_first_pages = TRUE; - - if (CURRENT_CHAIN (ogg)->pads == NULL) { - GST_ERROR_OBJECT (ogg, "broken ogg stream, chain has no BOS pages"); - return FALSE; - } - - FOR_PAD_IN_CURRENT_CHAIN (ogg, pad, if (pad->start == (guint64) - 1) - have_all_first_pages = FALSE;); - - if (have_all_first_pages) { - GST_DEBUG_OBJECT (ogg, - "SETUP_READ_BOS: no more bos pages, going to find end of stream"); - if (ogg->setup_state == SETUP_READ_FIRST_BOS) { - return gst_ogg_demux_set_setup_state (ogg, SETUP_FIND_LAST_CHAIN); - } else if (ogg->unordered) { - return gst_ogg_demux_set_setup_state (ogg, - SETUP_FIND_END_OF_LAST_STREAMS); - } else { - return gst_ogg_demux_set_setup_state (ogg, SETUP_FIND_END_OF_STREAMS); + pad->total_time = pad->last_time - pad->start_time; + chain->total_time = MAX (chain->total_time, pad->total_time); + chain->start_time = MIN (chain->start_time, pad->start_time); + chain->last_time = MAX (chain->last_time, pad->last_time); } - } else { - GstOggPad *pad = - gst_ogg_pad_get_in_current_chain (ogg, ogg_page_serialno (page)); - - gst_ogg_pad_populate (ogg, pad, page); + ogg->total_time += chain->total_time; } } - return TRUE; -} -static gboolean -_find_chain_get_unknown_part (GstOggDemux * ogg, gint64 * start, gint64 * end) -{ - *start = 0; - *end = G_MAXINT64; + /* dump our chains and streams */ + gst_ogg_print (ogg); - g_assert (ogg->current_chain >= 0); - FOR_PAD_IN_CURRENT_CHAIN (ogg, pad, *start = MAX (*start, pad->end_offset);); + /* activate first chain */ + if (ogg->chains->len > 0) { + GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, 0); - if (ogg->setup_state == SETUP_FIND_LAST_CHAIN) { - *end = gst_file_pad_get_length (ogg->sinkpad); - if (*end < 0) - return FALSE; - } else { - GSList *walk; - - g_assert (ogg->unordered != NULL); - for (walk = ogg->unordered; walk; walk = g_slist_next (walk)) { - GstOggPad *temp = walk->data; - - *end = MIN (*end, temp->start_offset); - } + gst_ogg_demux_activate_chain (ogg, chain); + /* and we are streaming now */ + ogg->state = OGG_STATE_STREAMING; } - GST_DEBUG_OBJECT (ogg, "we're looking for a new chain in the range [%" - G_GINT64_FORMAT ", %" G_GINT64_FORMAT "]", *start, *end); - /* overlapping chains?! */ - if (*end < *start) { - GST_ERROR_OBJECT (ogg, "chained streams overlap, bailing out"); + return TRUE; + + /*** error cases ***/ +no_peer: + { + GST_DEBUG ("we don't have a peer"); return FALSE; } - - return TRUE; -} - -static gboolean -_find_last_chain_init (GstOggDemux * ogg) -{ - gint64 end = gst_file_pad_get_length (ogg->sinkpad); - - ogg->seek_skipped = 0; - if (end < 0) +no_length: + { + GST_DEBUG ("can't get file length"); return FALSE; - if (!gst_ogg_demux_seek_before (ogg, end, 0)) + } +no_first_chain: + { + GST_DEBUG ("can't get first chain"); return FALSE; - return TRUE; -} - -static gboolean -_find_last_chain_process (GstOggDemux * ogg, ogg_page * page) -{ - GstOggPad *pad = gst_ogg_pad_get (ogg, page); - - /* optimization: set eos as found - we're investigating last pages here anyway */ - pad->end_found = TRUE; - /* set to 0 to indicate we found a page */ - ogg->seek_skipped = 0; - return TRUE; -} - -static gboolean -_find_chain_seek (GstOggDemux * ogg, gint64 start, gint64 end) -{ - if (end - start < SETUP_PASSTHROUGH_SIZE) { - GST_LOG_OBJECT (ogg, - "iterating through remaining window, because it's smaller than %u bytes", - SETUP_PASSTHROUGH_SIZE); - if (ogg->seek_to >= start) { - ogg->seek_skipped = 0; - if (!gst_ogg_demux_seek_before (ogg, start, start)) - return FALSE; - } - } else { - if (!gst_ogg_demux_seek_before (ogg, (start + end) / 2, start)) - return FALSE; } - return TRUE; } -static gboolean -_find_chain_init (GstOggDemux * ogg) -{ - gint64 start, end; - - ogg->seek_skipped = 0; - ogg->seek_to = -1; - if (!_find_chain_get_unknown_part (ogg, &start, &end)) - return FALSE; - if (!_find_chain_seek (ogg, start, end)) - return FALSE; - return TRUE; -} - -static gboolean -_find_chain_process (GstOggDemux * ogg, ogg_page * page) -{ - gint64 start, end; - - if (!_find_chain_get_unknown_part (ogg, &start, &end)) - return FALSE; - - if (ogg->seek_to <= start && gst_ogg_demux_position (ogg) > end) { - /* we now should have the first bos page, because - * - we seeked to a point in the known chain - * - we're now in a part that belongs to the unordered streams - */ - g_assert (g_slist_find (ogg->unordered, gst_ogg_pad_get (ogg, page))); - if (!ogg_page_bos (page)) { - /* broken stream */ - return FALSE; - } - if (!gst_ogg_demux_set_setup_state (ogg, SETUP_READ_BOS)) - return FALSE; - return _read_bos_process (ogg, page); - } else { - if (!_find_chain_seek (ogg, start, end)) - return FALSE; - } - - return TRUE; -} - -static gboolean -_find_streams_check (GstOggDemux * ogg) -{ - gint chain_nr = ogg->setup_state == SETUP_FIND_END_OF_LAST_STREAMS ? - ogg->chains->len - 1 : ogg->chains->len - 2; - gint64 endpos; - - /* figure out positions */ - if (ogg->setup_state == SETUP_FIND_END_OF_LAST_STREAMS) { - if ((endpos = gst_file_pad_get_length (ogg->sinkpad)) < 0) - return FALSE; - } else { - endpos = G_MAXINT64; - FOR_PAD_IN_CHAIN (ogg, pad, ogg->chains->len - 1, - endpos = MIN (endpos, pad->start_offset);); - } - if (!ogg->seek_skipped || gst_ogg_demux_position (ogg) >= endpos) { - /* have we found the endposition for all streams yet? */ - FOR_PAD_IN_CHAIN (ogg, pad, chain_nr, if (!pad->end_offset) - goto go_on;); - /* get out, we're done */ - ogg->seek_skipped = 0; - ogg->seek_to = -1; - if (ogg->unordered) { - ogg->setup_state = SETUP_FIND_END_OF_CHAIN; - } else { - gst_ogg_start_playing (ogg); - } - return TRUE; - go_on: - if (!gst_ogg_demux_seek_before (ogg, endpos, 0)) - return FALSE; - } - - return TRUE; -} - -static gboolean -_find_streams_init (GstOggDemux * ogg) -{ - ogg->seek_skipped = 0; - ogg->seek_to = -1; - return _find_streams_check (ogg); -} - -static gboolean -_find_streams_process (GstOggDemux * ogg, ogg_page * page) -{ - gint chain_nr = ogg->setup_state == SETUP_FIND_END_OF_LAST_STREAMS ? - ogg->chains->len - 1 : ogg->chains->len - 2; - - g_assert (ogg->setup_state == SETUP_FIND_END_OF_LAST_STREAMS || - ogg->setup_state == SETUP_FIND_END_OF_STREAMS); - g_assert (chain_nr >= 0); - /* mark current pad as having an endframe */ - if (ogg->seek_skipped) { - GstOggPad *pad = - gst_ogg_pad_get_in_chain (ogg, chain_nr, ogg_page_serialno (page)); - if (pad) { - pad->end_offset = TRUE; - g_print ("marking pad %d as having an end\n", pad->serial); - } - } - return _find_streams_check (ogg); -} - -#undef GST_CAT_DEFAULT -#define GST_CAT_DEFAULT gst_ogg_demux_debug - -static void -gst_ogg_demux_iterate (GstFilePad * pad) +/* streaming mode, receive a buffer, parse it, create pads for + * the serialno, submit pages and packets to the oggpads + */ +static GstFlowReturn +gst_ogg_demux_chain_unlocked (GstPad * pad, GstBuffer * buffer) { GstOggDemux *ogg; - guint8 *data; - guint available; - int pageout_ret = 1; - gint64 offset_end; + gint ret = -1; + GstFlowReturn result = GST_FLOW_OK; - ogg = GST_OGG_DEMUX (gst_pad_get_parent (GST_PAD (pad))); + ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad)); - available = gst_file_pad_available (ogg->sinkpad); - if (available == 0) { - if (gst_file_pad_eof (ogg->sinkpad)) { - gst_ogg_demux_eos (ogg); - } else { - GST_DEBUG_OBJECT (ogg, "no data available, doing nothing"); - } - if (ogg->state != GST_OGG_STATE_SETUP) - return; - } - GST_LOG_OBJECT (ogg, "queueing next %u bytes of data", available); - data = (guint8 *) ogg_sync_buffer (&ogg->sync, available); - if ((available = gst_file_pad_read (ogg->sinkpad, data, available)) < 0) { - GST_ERROR_OBJECT (ogg, "error %u reading data from pad", - gst_file_pad_error (ogg->sinkpad)); - return; - } - if (ogg_sync_wrote (&ogg->sync, available) != 0) { - GST_ELEMENT_ERROR (ogg, LIBRARY, TOO_LAZY, (NULL), - ("ogg_sync_wrote failed")); - return; - } - offset_end = gst_file_pad_tell (ogg->sinkpad); - g_assert (offset_end >= 0); /* FIXME: do sth reasonable if no length available */ - while (pageout_ret != 0) { + GST_DEBUG ("chain"); + gst_ogg_demux_submit_buffer (ogg, buffer); + + while (ret != 0 && result == GST_FLOW_OK) { ogg_page page; - pageout_ret = ogg_sync_pageout (&ogg->sync, &page); - switch (pageout_ret) { - case -1: - /* FIXME: need some kind of discont here, we don't know any values to send though, - * we only have the END_OFFSET */ - break; - case 0: - if (ogg->state == GST_OGG_STATE_SETUP) { - if (gst_file_pad_get_length (ogg->sinkpad) <= offset_end) { - if (ogg->seek_skipped) { - if (!gst_ogg_demux_seek_before (ogg, - gst_file_pad_get_length (ogg->sinkpad), 0)) - abort_setup (ogg); - } else if (ogg->setup_state == SETUP_FIND_LAST_CHAIN) { - if (ogg->unordered) { - if (!gst_ogg_demux_seek_before (ogg, offset_end / 2, 0)) - abort_setup (ogg); - if (!gst_ogg_demux_set_setup_state (ogg, - SETUP_FIND_END_OF_CHAIN)) - goto out; - } else { - if (!gst_ogg_demux_set_setup_state (ogg, - SETUP_FIND_END_OF_LAST_STREAMS)) - goto out; - } - } else { - abort_setup (ogg); - } - goto out; + ret = ogg_sync_pageout (&ogg->sync, &page); + if (ret == 0) + /* need more data */ + break; + if (ret == -1) { + /* discontinuity in the pages */ + } else { + GstOggPad *pad; + guint serialno; + + serialno = ogg_page_serialno (&page); + + GST_LOG_OBJECT (ogg, + "processing ogg page (serial %08lx, pageno %ld, granule pos %llu, bos %d)", + serialno, ogg_page_pageno (&page), + ogg_page_granulepos (&page), ogg_page_bos (&page)); + + if (ogg_page_bos (&page)) { + GstOggChain *chain; + + /* first page */ + /* see if we know about the chain already */ + chain = gst_ogg_demux_find_chain (ogg, serialno); + if (chain) { + /* we do, activate it */ + gst_ogg_demux_activate_chain (ogg, chain); + pad = gst_ogg_demux_find_pad (ogg, serialno); + } else { + /* chain is unknown */ + if (ogg->state == OGG_STATE_STREAMING) { + /* remove existing pads */ + gst_ogg_demux_deactivate_current_chain (ogg); + /* switch to new-chain mode */ + ogg->state = OGG_STATE_NEW_CHAIN; } + if (ogg->building_chain == NULL) { + ogg->building_chain = gst_ogg_chain_new (ogg); + ogg->building_chain->offset = 0; + } + pad = gst_ogg_chain_new_stream (ogg->building_chain, serialno); } - break; - case 1: - GST_LOG_OBJECT (ogg, - "processing ogg page (serial %d, packet %ld, granule pos %llu, state: %d, bos %d)", - ogg_page_serialno (&page), ogg_page_pageno (&page), - ogg_page_granulepos (&page), ogg->state, ogg_page_bos (&page)); - switch (ogg->state) { - case GST_OGG_STATE_SETUP: - if (!setup_funcs[ogg->setup_state].process (ogg, &page)) { - abort_setup (ogg); - goto out; - } - break; - case GST_OGG_STATE_START: - if (gst_file_pad_seek (ogg->sinkpad, 0, GST_SEEK_METHOD_END) == 0) { - GST_OGG_SET_STATE (ogg, GST_OGG_STATE_SETUP); - GST_DEBUG_OBJECT (ogg, "stream can seek, try setup now"); - if (gst_file_pad_seek (ogg->sinkpad, 0, GST_SEEK_METHOD_SET) != 0) { - GST_ELEMENT_ERROR (ogg, CORE, SEEK, (NULL), - ("stream can seek to end, but not to start. Can't handle that.")); - } - ogg_sync_clear (&ogg->sync); - gst_ogg_add_chain (ogg); - GST_FLAG_SET (ogg, GST_OGG_FLAG_WAIT_FOR_DISCONT); - goto out; - } else { - GST_DEBUG_OBJECT (ogg, "stream can not seek"); - gst_ogg_add_chain (ogg); - GST_OGG_SET_STATE (ogg, GST_OGG_STATE_PLAY); - } - /* fall through */ - case GST_OGG_STATE_SEEK: - case GST_OGG_STATE_PLAY: - gst_ogg_demux_push (ogg, &page); - break; - default: - g_assert_not_reached (); - break; + } else { + /* no more bos pages, see if we need to activate the chain we were + * building */ + if (ogg->building_chain) { + gst_ogg_demux_activate_chain (ogg, ogg->building_chain); + ogg->building_chain = NULL; + ogg->state = OGG_STATE_STREAMING; } - break; - default: - GST_WARNING_OBJECT (ogg, - "unknown return value %d from ogg_sync_pageout", pageout_ret); - pageout_ret = 0; - break; + pad = gst_ogg_demux_find_pad (ogg, serialno); + } + if (pad) { + result = gst_ogg_pad_submit_page (pad, &page); + } else { + GST_LOG_OBJECT (ogg, "cannot find pad for serial %08lx", serialno); + } } } -out: - return; + gst_buffer_unref (buffer); + + return result; } -static GstOggPad * -gst_ogg_pad_new (GstOggDemux * ogg, int serial) + +static GstFlowReturn +gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer) { - GstOggPad *ret = g_new0 (GstOggPad, 1); - GstTagList *list = gst_tag_list_new (); + GstFlowReturn ret; - ret->serial = serial; - if (ogg_stream_init (&ret->stream, serial) != 0) { - GST_ERROR_OBJECT (ogg, - "Could not initialize ogg_stream struct for serial %d.", serial); - g_free (ret); - return NULL; - } - gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_SERIAL, serial, NULL); - gst_element_found_tags (GST_ELEMENT (ogg), list); - gst_tag_list_free (list); - - GST_LOG_OBJECT (ogg, "created new ogg src %p for stream with serial %d", ret, - serial); - ret->start_offset = ret->end_offset = -1; - ret->start = -1; - ret->start_found = ret->end_found = FALSE; - ret->offset = GST_BUFFER_OFFSET_NONE; + GST_STREAM_LOCK (pad); + ret = gst_ogg_demux_chain_unlocked (pad, buffer); + GST_STREAM_UNLOCK (pad); return ret; } -static void -gst_ogg_pad_remove (GstOggDemux * ogg, GstOggPad * pad) -{ - if (pad->pad) { - /* FIXME: - * we do it in the EOS signal already - EOS handling needs to be better thought out. - * Correct way would be pushing EOS on eos page, but scheduler doesn't like that - */ - if (GST_PAD_IS_USABLE (pad->pad)) - gst_pad_push (pad->pad, GST_DATA (gst_event_new (GST_EVENT_EOS))); - gst_element_remove_pad (GST_ELEMENT (ogg), pad->pad); - pad->pad = NULL; - } - if (ogg_stream_clear (&pad->stream) != 0) - GST_ERROR_OBJECT (ogg, - "ogg_stream_clear (serial %d) did not return 0, ignoring this error", - pad->serial); - GST_LOG_OBJECT (ogg, "free ogg src %p for stream with serial %d", pad, - pad->serial); - g_free (pad); -} static void -gst_ogg_demux_push (GstOggDemux * ogg, ogg_page * page) +gst_ogg_demux_send_eos (GstOggDemux * ogg) { - GstOggPad *cur = - gst_ogg_pad_get_in_current_chain (ogg, ogg_page_serialno (page)); + GstOggChain *chain = ogg->current_chain; - /* now we either have a stream (cur) or not */ - if (ogg_page_bos (page)) { - if (cur) { - GST_DEBUG_OBJECT (ogg, - "ogg page declared as BOS while stream %d already existed." - "Possibly a seek happened.", cur->serial); - } else if (cur) { - GST_DEBUG_OBJECT (ogg, "reactivating deactivated stream %d.", - cur->serial); - } else { - /* FIXME: monitor if we are still in creation stage? */ - cur = gst_ogg_pad_new (ogg, ogg_page_serialno (page)); - if (!cur) { - GST_ELEMENT_ERROR (ogg, LIBRARY, TOO_LAZY, (NULL), - ("Creating ogg_stream struct failed.")); - return; - } - if (ogg->current_chain == -1) { - /* add new one at the end */ - gst_ogg_add_chain (ogg); - } - CURRENT_CHAIN (ogg)->pads = - g_slist_prepend (CURRENT_CHAIN (ogg)->pads, cur); + if (chain) { + gint i; + + for (i = 0; i < chain->streams->len; i++) { + GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i); + + gst_pad_push_event (GST_PAD (pad), gst_event_new (GST_EVENT_EOS)); } - ogg->bos = TRUE; - } else if (ogg->bos) { - gst_element_no_more_pads (GST_ELEMENT (ogg)); - ogg->bos = FALSE; } - if (cur == NULL) { - GST_ELEMENT_ERROR (ogg, STREAM, DECODE, (NULL), - ("invalid ogg stream serial no")); - return; +} + +/* random access code + * + * - first find all the chains and streams by scanning the + * file. + * - then get and chain buffers, just like the streaming + * case. + * - when seeking, we can use the chain info to perform the + * seek. + */ +static void +gst_ogg_demux_loop (GstOggPad * pad) +{ + GstOggDemux *ogg; + GstFlowReturn ret; + GstBuffer *buffer; + + ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad)); + + GST_STREAM_LOCK (pad); + if (ogg->need_chains) { + gboolean got_chains; + + /* this is the only place where we write chains */ + GST_CHAIN_LOCK (ogg); + got_chains = gst_ogg_demux_find_chains (ogg); + GST_CHAIN_UNLOCK (ogg); + if (!got_chains) { + GST_LOG_OBJECT (ogg, "could not read chains"); + goto pause; + } + ogg->need_chains = FALSE; + ogg->offset = 0; } - if (ogg_stream_pagein (&cur->stream, page) != 0) { - GST_WARNING_OBJECT (ogg, - "ogg stream choked on page (serial %d), resetting stream", cur->serial); - gst_ogg_pad_reset (ogg, cur); - return; + + GST_LOG_OBJECT (ogg, "pull data %lld", ogg->offset); + if (ogg->offset == ogg->length) { + gst_ogg_demux_send_eos (ogg); + goto pause; } - switch (ogg->state) { - case GST_OGG_STATE_SEEK: - GST_LOG_OBJECT (ogg, - "in seek - offset now: %" G_GUINT64_FORMAT - " (pad %d) - desired offset %" G_GUINT64_FORMAT " (pad %d)", - cur->known_offset, cur->serial, ogg->seek_to, ogg->seek_pad->serial); - if (cur != ogg->seek_pad) { - break; - } else { - gint64 position, diff; - gdouble ratio; + ret = gst_pad_pull_range (ogg->sinkpad, ogg->offset, CHUNKSIZE, &buffer); + if (ret != GST_FLOW_OK) { + GST_LOG_OBJECT (ogg, "got unexpected %d", ret); + goto pause; + } - /* see if we reached the destination position when seeking */ - position = get_relative (ogg, cur, ogg_page_granulepos (page), - GST_FORMAT_TIME); - /* Note: ogg->seek_to is already in GST_FORMAT_TIME... */ - if (position == -1) { - /* let's just stop then */ - goto play; - } + ogg->offset += GST_BUFFER_SIZE (buffer); - /* fairly random treshold. */ - if (ogg->seek_to > position) - diff = ogg->seek_to - position; - else - diff = position - ogg->seek_to; - if (diff < GST_SECOND) { - GST_DEBUG ("Close enough (%" GST_TIME_FORMAT " seconds off)", - GST_TIME_ARGS (diff)); - ogg->seek_to = position; - goto play; - } + ret = gst_ogg_demux_chain_unlocked (ogg->sinkpad, buffer); + if (ret != GST_FLOW_OK) { + GST_LOG_OBJECT (ogg, "got unexpected %d, pausing", ret); + goto pause; + } - /* not too long */ - if (ogg->seek_try > 5) { - GST_DEBUG ("Seeking took too long, continuing with current page"); - ogg->seek_to = position; - goto play; - } + GST_STREAM_UNLOCK (pad); + return; - /* seek again! yay */ - ratio = (gdouble) ogg->seek_to / position; - ogg->seek_offset = ogg->seek_offset * ratio; - if (gst_file_pad_seek (ogg->sinkpad, ogg->seek_offset, - GST_SEEK_METHOD_SET) != 0) { - goto play; - } - ogg->seek_try++; - ogg_sync_clear (&ogg->sync); - return; +pause: + GST_LOG_OBJECT (ogg, "pausing task"); + gst_task_pause (GST_RPAD_TASK (ogg->sinkpad)); + GST_STREAM_UNLOCK (pad); + return; +} - play: - GST_OGG_SET_STATE (ogg, GST_OGG_STATE_PLAY); - GST_DEBUG_OBJECT (ogg, - "ended seek at offset %" G_GUINT64_FORMAT " (requested %" - G_GUINT64_FORMAT, cur->known_offset, ogg->seek_to); - ogg->seek_pad = NULL; - ogg->seek_offset = 0; - ogg->seek_try = 0; - } - /* fallthrough */ - case GST_OGG_STATE_PLAY: - cur->known_offset = ogg_page_granulepos (page); - gst_ogg_pad_push (ogg, cur); +static gboolean +gst_ogg_demux_sink_activate (GstPad * sinkpad, GstActivateMode mode) +{ + gboolean result = FALSE; + GstOggDemux *ogg; + + ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (sinkpad)); + + switch (mode) { + case GST_ACTIVATE_PUSH: + ogg->seekable = FALSE; + result = TRUE; break; - default: - g_assert_not_reached (); + case GST_ACTIVATE_PULL: + /* if we have a scheduler we can start the task */ + if (GST_ELEMENT_SCHEDULER (ogg)) { + gst_pad_peer_set_active (sinkpad, mode); + GST_STREAM_LOCK (sinkpad); + GST_RPAD_TASK (sinkpad) = + gst_scheduler_create_task (GST_ELEMENT_SCHEDULER (ogg), + (GstTaskFunction) gst_ogg_demux_loop, sinkpad); + + ogg->need_chains = TRUE; + ogg->seekable = TRUE; + gst_task_start (GST_RPAD_TASK (sinkpad)); + GST_STREAM_UNLOCK (sinkpad); + result = TRUE; + } + break; + case GST_ACTIVATE_NONE: + /* step 1, unblock clock sync (if any) */ + + /* step 2, make sure streaming finishes */ + GST_STREAM_LOCK (sinkpad); + + /* step 3, stop the task */ + if (GST_RPAD_TASK (sinkpad)) { + gst_task_stop (GST_RPAD_TASK (sinkpad)); + gst_object_unref (GST_OBJECT (GST_RPAD_TASK (sinkpad))); + GST_RPAD_TASK (sinkpad) = NULL; + } + GST_STREAM_UNLOCK (sinkpad); + + result = TRUE; break; } - if (ogg_page_eos (page)) { - GST_DEBUG_OBJECT (ogg, "got EOS for stream with serial %d, sending EOS now", - cur->serial); - - /* send an EOS before removing this pad */ - if (GST_PAD_IS_USABLE (cur->pad)) - gst_pad_push (cur->pad, GST_DATA (gst_event_new (GST_EVENT_EOS))); - - gst_element_remove_pad (GST_ELEMENT (ogg), cur->pad); - cur->pad = NULL; - } -} - -static void -gst_ogg_sync (GstOggDemux * ogg, GstOggPad * cur) -{ - gint64 bias, time; - GstFormat fmt = GST_FORMAT_TIME; - - time = get_relative (ogg, cur, cur->offset, GST_FORMAT_TIME); - FOR_PAD_IN_CURRENT_CHAIN (ogg, pad, - if (pad->pad && GST_PAD_PEER (pad->pad) && - pad->offset != GST_BUFFER_OFFSET_NONE) { - if (gst_pad_query (GST_PAD_PEER (pad->pad), - GST_QUERY_POSITION, &fmt, &bias) && bias + GST_SECOND < time) { - GstEvent * event; - gint64 val = 0; - GstFormat fmt = GST_FORMAT_DEFAULT; - event = gst_event_new_filler_stamped (bias, - time - bias - GST_SECOND / 2); - GST_DEBUG ("Syncing stream %d at time %" GST_TIME_FORMAT - " and duration %" GST_TIME_FORMAT, - pad->serial, GST_TIME_ARGS (bias), - GST_TIME_ARGS (time - bias - GST_SECOND / 2)); - gst_pad_push (pad->pad, GST_DATA (event)); - gst_pad_convert (GST_PAD_PEER (pad->pad), - GST_FORMAT_TIME, bias, &fmt, &val); - /* hmm... */ - pad->offset = pad->start + val;} - } - ); -} - -static void -gst_ogg_pad_push (GstOggDemux * ogg, GstOggPad * pad) -{ - ogg_packet packet; - int ret; - GstBuffer *buf; - - /* Hack. If someone connects to the push (or any related) signal and - * goes to READY, the pad will be destroyed and the next line will - * segfault. This does not work on PLAY -> READY -> PLAY. */ - while (GST_STATE (ogg) >= GST_STATE_PAUSED) { - ret = ogg_stream_packetout (&pad->stream, &packet); - GST_LOG_OBJECT (ogg, "packetout gave %d", ret); - switch (ret) { - case 0: - return; - case -1: - /* out of sync, could call gst_ogg_pad_reset() here but ogg can decode - * the packet just fine. We should probably send a DISCONT though. */ - break; - case 1:{ - gint64 pos = -1; - - /* only push data when playing, not during seek or similar */ - if (ogg->state != GST_OGG_STATE_PLAY) - continue; - if (!pad->pad) { - GstCaps *caps = gst_ogg_type_find (&packet); - gchar *name = g_strdup_printf ("serial_%d", pad->serial); - - if (caps == NULL) { - GST_WARNING_OBJECT (ogg, - "couldn't find caps for stream with serial %d", pad->serial); - caps = gst_caps_new_simple ("application/octet-stream", NULL); - } - pad->pad = - gst_pad_new_from_template (gst_static_pad_template_get - (&ogg_demux_src_template_factory), name); - g_free (name); - gst_pad_set_event_function (pad->pad, - GST_DEBUG_FUNCPTR (gst_ogg_demux_src_event)); - gst_pad_set_event_mask_function (pad->pad, - GST_DEBUG_FUNCPTR (gst_ogg_demux_get_event_masks)); - gst_pad_set_query_function (pad->pad, - GST_DEBUG_FUNCPTR (gst_ogg_demux_src_query)); - gst_pad_set_query_type_function (pad->pad, - GST_DEBUG_FUNCPTR (gst_ogg_demux_get_query_types)); - gst_pad_set_formats_function (pad->pad, - GST_DEBUG_FUNCPTR (gst_ogg_demux_get_formats)); - gst_pad_set_convert_function (pad->pad, - GST_DEBUG_FUNCPTR (gst_ogg_demux_src_convert)); - - gst_pad_use_explicit_caps (pad->pad); - gst_pad_set_explicit_caps (pad->pad, caps); - gst_caps_free (caps); - gst_pad_set_active (pad->pad, TRUE); - gst_element_add_pad (GST_ELEMENT (ogg), pad->pad); - } - /* check for discont */ - if (packet.packetno != pad->packetno++) { - pad->flags |= GST_OGG_PAD_NEEDS_DISCONT; - pad->packetno = packet.packetno + 1; - } - - if (pad->known_offset != -1) { - pos = get_relative (ogg, pad, pad->known_offset, GST_FORMAT_DEFAULT); - } - - if ((pad->flags & GST_OGG_PAD_NEEDS_FLUSH) - && GST_PAD_IS_USABLE (pad->pad)) { - gst_pad_push (pad->pad, GST_DATA (gst_event_new (GST_EVENT_FLUSH))); - pad->flags &= (~GST_OGG_PAD_NEEDS_FLUSH); - } - - /* send discont if needed */ - if ((pad->flags & GST_OGG_PAD_NEEDS_DISCONT) - && GST_PAD_IS_USABLE (pad->pad)) { - /* so in order to synchronized the variety of streams, we will - * not use the granpos but the last seekpos for the discont. */ - GstFormat fmt; - GstEvent *event; - gint64 discont; - - if (pos != -1) { - fmt = GST_FORMAT_DEFAULT; - if (!GST_PAD_PEER (pad->pad) || - !gst_pad_convert (GST_PAD_PEER (pad->pad), - ogg->seek_format, ogg->seek_to, &fmt, &discont)) { - event = gst_event_new_discontinuous (FALSE, - ogg->seek_format, ogg->seek_to, GST_FORMAT_UNDEFINED); - } else { - event = gst_event_new_discontinuous (FALSE, - GST_FORMAT_DEFAULT, discont, GST_FORMAT_UNDEFINED); - pad->offset = discont; - } - } else { - event = gst_event_new_discontinuous (FALSE, - GST_FORMAT_DEFAULT, 0, GST_FORMAT_UNDEFINED); - } - - /* FIXME: this might be wrong because we can only use the last - * known offset */ - - gst_pad_push (pad->pad, GST_DATA (event)); - pad->flags &= (~GST_OGG_PAD_NEEDS_DISCONT); - } - - /* optimization: use a bufferpool containing the ogg packet? */ - buf = - gst_pad_alloc_buffer (pad->pad, GST_BUFFER_OFFSET_NONE, - packet.bytes); - memcpy (buf->data, packet.packet, packet.bytes); - if (pad->offset != -1) - GST_BUFFER_OFFSET (buf) = pad->offset; - if (packet.granulepos != -1 && pos != -1) - GST_BUFFER_OFFSET_END (buf) = pos; - pad->offset = packet.granulepos; - gst_ogg_sync (ogg, pad); - if (GST_PAD_IS_USABLE (pad->pad)) - gst_pad_push (pad->pad, GST_DATA (buf)); - break; - } - default: - GST_ERROR_OBJECT (ogg, - "invalid return value %d for ogg_stream_packetout, resetting stream", - ret); - gst_ogg_pad_reset (ogg, pad); - break; - } - } -} -static void -gst_ogg_pad_reset (GstOggDemux * ogg, GstOggPad * pad) -{ - ogg_stream_reset (&pad->stream); - pad->offset = GST_BUFFER_OFFSET_NONE; - /* FIXME: need a discont here */ -} - -static void -gst_ogg_chains_clear (GstOggDemux * ogg) -{ - gint i; - GSList *walk; - - for (i = ogg->chains->len - 1; i >= 0; i--) { - GstOggChain *cur = &g_array_index (ogg->chains, GstOggChain, i); - - for (walk = cur->pads; walk; walk = g_slist_next (walk)) { - gst_ogg_pad_remove (ogg, (GstOggPad *) walk->data); - } - g_slist_free (cur->pads); - cur->pads = NULL; - g_array_remove_index (ogg->chains, i); - } - ogg->current_chain = -1; + return result; } static GstElementStateReturn gst_ogg_demux_change_state (GstElement * element) { GstOggDemux *ogg; + GstElementStateReturn result = GST_STATE_FAILURE; ogg = GST_OGG_DEMUX (element); @@ -1661,13 +2018,14 @@ gst_ogg_demux_change_state (GstElement * element) break; case GST_STATE_PAUSED_TO_PLAYING: break; + } + + result = parent_class->change_state (element); + + switch (GST_STATE_TRANSITION (element)) { case GST_STATE_PLAYING_TO_PAUSED: break; case GST_STATE_PAUSED_TO_READY: - gst_ogg_chains_clear (ogg); - GST_OGG_SET_STATE (ogg, GST_OGG_STATE_START); - ogg->seek_pad = NULL; - ogg->seek_to = 0; break; case GST_STATE_READY_TO_NULL: ogg_sync_clear (&ogg->sync); @@ -1675,8 +2033,7 @@ gst_ogg_demux_change_state (GstElement * element) default: break; } - - return parent_class->change_state (element); + return result; } /*** typefinding **************************************************************/ @@ -1691,6 +2048,7 @@ typedef struct GstCaps *caps; } OggTypeFind; + static guint8 * ogg_find_peek (gpointer data, gint64 offset, guint size) { @@ -1769,38 +2127,40 @@ gst_ogg_print (GstOggDemux * ogg) #else /* !GST_DISABLE_GST_DEBUG */ -#define gst_ogg_print_pad(ogg, _pad) \ -G_STMT_START{\ - GstOggPad *pad = (_pad); \ - GST_INFO_OBJECT (ogg, " stream %d:", pad->serial); \ - GST_INFO_OBJECT (ogg, " length %" G_GUINT64_FORMAT, pad->length); \ - GST_INFO_OBJECT (ogg, " pages %ld", pad->pages); \ - GST_INFO_OBJECT (ogg, " offset: %"G_GINT64_FORMAT"%s - %"G_GINT64_FORMAT"%s", \ - pad->start_offset, pad->start_found ? "" : " (?)", \ - pad->end_offset, pad->end_found ? "" : " (?)"); \ -}G_STMT_END static void gst_ogg_print (GstOggDemux * ogg) { - guint i; - GSList *walk; + guint j, i; + + GST_INFO_OBJECT (ogg, "%u chains, total time %" GST_TIME_FORMAT ":", + ogg->chains->len, GST_TIME_ARGS (ogg->total_time)); for (i = 0; i < ogg->chains->len; i++) { - GstOggChain *chain = &g_array_index (ogg->chains, GstOggChain, i); + GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i); - GST_INFO_OBJECT (ogg, "chain %d (%u streams):", i, - g_slist_length (chain->pads)); - for (walk = chain->pads; walk; walk = g_slist_next (walk)) { - gst_ogg_print_pad (ogg, walk->data); + GST_INFO_OBJECT (ogg, " chain %d (%u streams):", i, chain->streams->len); + GST_INFO_OBJECT (ogg, " offset: %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT, + chain->offset, chain->end_offset); + GST_INFO_OBJECT (ogg, " total time: %" GST_TIME_FORMAT, + GST_TIME_ARGS (chain->total_time)); + + for (j = 0; j < chain->streams->len; j++) { + GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, j); + + GST_INFO_OBJECT (ogg, " stream %08lx:", stream->serialno); + GST_INFO_OBJECT (ogg, " start time %" GST_TIME_FORMAT ":", + GST_TIME_ARGS (stream->start_time)); + GST_INFO_OBJECT (ogg, " first granulepos %" G_GINT64_FORMAT ":", + stream->first_granule); + GST_INFO_OBJECT (ogg, " first time %" GST_TIME_FORMAT ":", + GST_TIME_ARGS (stream->first_time)); + GST_INFO_OBJECT (ogg, " last granulepos %" G_GINT64_FORMAT ":", + stream->last_granule); + GST_INFO_OBJECT (ogg, " last time %" GST_TIME_FORMAT ":", + GST_TIME_ARGS (stream->last_time)); + GST_INFO_OBJECT (ogg, " total time %" GST_TIME_FORMAT ":", + GST_TIME_ARGS (stream->total_time)); } } - if (ogg->unordered) { - GST_INFO_OBJECT (ogg, "unordered (%u streams):", i, - g_slist_length (ogg->unordered)); - for (walk = ogg->unordered; walk; walk = g_slist_next (walk)) { - gst_ogg_print_pad (ogg, walk->data); - } - } - } #endif /* GST_DISABLE_GST_DEBUG */ diff --git a/ext/ogg/gstoggmux.c b/ext/ogg/gstoggmux.c index 9c13e5ecb3..149c5a3d1a 100644 --- a/ext/ogg/gstoggmux.c +++ b/ext/ogg/gstoggmux.c @@ -219,6 +219,9 @@ gst_ogg_mux_class_init (GstOggMuxClass * klass) parent_class = g_type_class_ref (GST_TYPE_ELEMENT); + gobject_class->get_property = gst_ogg_mux_get_property; + gobject_class->set_property = gst_ogg_mux_set_property; + gstelement_class->request_new_pad = gst_ogg_mux_request_new_pad; g_object_class_install_property (gobject_class, ARG_MAX_DELAY, @@ -232,8 +235,6 @@ gst_ogg_mux_class_init (GstOggMuxClass * klass) gstelement_class->change_state = gst_ogg_mux_change_state; - gstelement_class->get_property = gst_ogg_mux_get_property; - gstelement_class->set_property = gst_ogg_mux_set_property; } static const GstEventMask * @@ -259,7 +260,6 @@ gst_ogg_mux_init (GstOggMux * ogg_mux) gst_pad_set_event_function (ogg_mux->srcpad, gst_ogg_mux_handle_src_event); gst_element_add_pad (GST_ELEMENT (ogg_mux), ogg_mux->srcpad); - GST_FLAG_SET (GST_ELEMENT (ogg_mux), GST_ELEMENT_EVENT_AWARE); GST_FLAG_SET (GST_ELEMENT (ogg_mux), GST_OGG_FLAG_BOS); /* seed random number generator for creation of serial numbers */ @@ -273,24 +273,20 @@ gst_ogg_mux_init (GstOggMux * ogg_mux) ogg_mux->delta_pad = NULL; - gst_element_set_loop_function (GST_ELEMENT (ogg_mux), gst_ogg_mux_loop); + //gst_element_set_loop_function (GST_ELEMENT (ogg_mux), gst_ogg_mux_loop); + gst_ogg_mux_loop (GST_ELEMENT (ogg_mux)); } static GstPadLinkReturn -gst_ogg_mux_sinkconnect (GstPad * pad, const GstCaps * vscaps) +gst_ogg_mux_sinkconnect (GstPad * pad, GstPad * peer) { GstOggMux *ogg_mux; - GstStructure *structure; - const gchar *mimetype; ogg_mux = GST_OGG_MUX (gst_pad_get_parent (pad)); GST_DEBUG_OBJECT (ogg_mux, "sinkconnect triggered on %s", gst_pad_get_name (pad)); - structure = gst_caps_get_structure (vscaps, 0); - mimetype = gst_structure_get_name (structure); - return GST_PAD_LINK_OK; } @@ -412,9 +408,10 @@ static GstBuffer * gst_ogg_mux_next_buffer (GstOggPad * pad, gboolean * interrupt) { GstData *data = NULL; + GstBuffer *buffer = NULL; - while (data == NULL) { - data = gst_pad_pull (pad->pad); + while (buffer == NULL) { + //gst_pad_pull (pad->pad, &buffer); GST_DEBUG ("muxer: pulled %s:%s %p", GST_DEBUG_PAD_NAME (pad->pad), data); /* if it's an event, handle it */ if (GST_IS_EVENT (data)) { @@ -430,23 +427,18 @@ gst_ogg_mux_next_buffer (GstOggPad * pad, gboolean * interrupt) pad->eos = TRUE; gst_event_unref (event); return NULL; - case GST_EVENT_INTERRUPT: - *interrupt = TRUE; - return NULL; case GST_EVENT_DISCONTINUOUS: { - guint64 value; + guint64 start_value, end_value; - if (GST_EVENT_DISCONT_NEW_MEDIA (event)) { - gst_pad_event_default (pad->pad, event); - break; - } - if (gst_event_discont_get_value (event, GST_FORMAT_TIME, &value)) { + if (gst_event_discont_get_value (event, GST_FORMAT_TIME, + &start_value, &end_value)) { GST_DEBUG_OBJECT (ogg_mux, - "got discont of %" G_GUINT64_FORMAT " on pad %s:%s", - value, GST_DEBUG_PAD_NAME (pad->pad)); + "got discont of %" G_GUINT64_FORMAT " and %" G_GUINT64_FORMAT + " on pad %s:%s", start_value, end_value, + GST_DEBUG_PAD_NAME (pad->pad)); } - pad->offset = value; + pad->offset = start_value; gst_event_unref (event); } @@ -489,7 +481,7 @@ gst_ogg_mux_buffer_from_page (GstOggMux * mux, ogg_page * page, gboolean delta) /* allocate space for header and body */ buffer = gst_pad_alloc_buffer (mux->srcpad, GST_BUFFER_OFFSET_NONE, - page->header_len + page->body_len); + page->header_len + page->body_len, NULL); memcpy (GST_BUFFER_DATA (buffer), page->header, page->header_len); memcpy (GST_BUFFER_DATA (buffer) + page->header_len, page->body, page->body_len); @@ -511,7 +503,7 @@ gst_ogg_mux_push_page (GstOggMux * mux, ogg_page * page, gboolean delta) if (GST_PAD_IS_USABLE (mux->srcpad)) { GstBuffer *buffer = gst_ogg_mux_buffer_from_page (mux, page, delta); - gst_pad_push (mux->srcpad, GST_DATA (buffer)); + gst_pad_push (mux->srcpad, buffer); } } @@ -817,7 +809,7 @@ gst_ogg_mux_send_headers (GstOggMux * mux) caps = gst_pad_get_caps (mux->srcpad); if (caps) { gst_ogg_mux_set_header_on_caps (caps, hbufs); - gst_pad_try_set_caps (mux->srcpad, caps); + //gst_pad_try_set_caps (mux->srcpad, caps); } /* and send the buffers */ hwalk = hbufs; @@ -827,7 +819,7 @@ gst_ogg_mux_send_headers (GstOggMux * mux) hwalk = hwalk->next; if (GST_PAD_IS_USABLE (mux->srcpad)) { - gst_pad_push (mux->srcpad, GST_DATA (buf)); + gst_pad_push (mux->srcpad, buf); } else { gst_buffer_unref (buf); } @@ -897,9 +889,8 @@ gst_ogg_mux_loop (GstElement * element) } else { /* no pad to pull on, send EOS */ if (GST_PAD_IS_USABLE (ogg_mux->srcpad)) - gst_pad_push (ogg_mux->srcpad, - GST_DATA (gst_event_new (GST_EVENT_EOS))); - gst_element_set_eos (element); + gst_pad_push_event (ogg_mux->srcpad, gst_event_new (GST_EVENT_EOS)); + //gst_element_set_eos (element); return; } } @@ -1013,11 +1004,6 @@ gst_ogg_mux_loop (GstElement * element) pad->prev_delta = delta_unit; /* swap the packet in */ - if (packet.e_o_s == 1) - GST_DEBUG_OBJECT (pad, "swapping in EOS packet"); - if (packet.b_o_s == 1) - GST_DEBUG_OBJECT (pad, "swapping in BOS packet"); - ogg_stream_packetin (&pad->stream, &packet); /* don't need the old buffer anymore */ diff --git a/ext/ogg/gstogmparse.c b/ext/ogg/gstogmparse.c index cef1f8c94e..49e6c5a3ea 100644 --- a/ext/ogg/gstogmparse.c +++ b/ext/ogg/gstogmparse.c @@ -147,7 +147,7 @@ static gboolean gst_ogm_parse_sink_convert (GstPad * pad, GstFormat src_format, static gboolean gst_ogm_parse_sink_query (GstPad * pad, GstQueryType type, GstFormat * fmt, gint64 * val); -static void gst_ogm_parse_chain (GstPad * pad, GstData * data); +static GstFlowReturn gst_ogm_parse_chain (GstPad * pad, GstBuffer * buffer); static GstElementStateReturn gst_ogm_parse_change_state (GstElement * element); @@ -499,11 +499,11 @@ gst_ogm_parse_sink_query (GstPad * pad, GST_FORMAT_DEFAULT, ogm->next_granulepos, fmt, val); } -static void -gst_ogm_parse_chain (GstPad * pad, GstData * dat) +static GstFlowReturn +gst_ogm_parse_chain (GstPad * pad, GstBuffer * buffer) { GstOgmParse *ogm = GST_OGM_PARSE (gst_pad_get_parent (pad)); - GstBuffer *buf = GST_BUFFER (dat); + GstBuffer *buf = GST_BUFFER (buffer); guint8 *data = GST_BUFFER_DATA (buf); guint size = GST_BUFFER_SIZE (buf); @@ -567,7 +567,7 @@ gst_ogm_parse_chain (GstPad * pad, GstData * dat) fcc = GST_MAKE_FOURCC (ogm->hdr.subtype[0], ogm->hdr.subtype[1], ogm->hdr.subtype[2], ogm->hdr.subtype[3]); - GST_LOG_OBJECT (ogm, "Type: %s, subtype: " GST_FOURCC_FORMAT + GST_LOG_OBJECT (ogm, "Type: %s, subtype: %" GST_FOURCC_FORMAT ", size: %dx%d, timeunit: %" G_GINT64_FORMAT " (fps: %lf), s/u: %" G_GINT64_FORMAT ", " "def.len: %d, bufsize: %d, bps: %d", @@ -595,13 +595,13 @@ gst_ogm_parse_chain (GstPad * pad, GstData * dat) } ogm->srcpad = gst_pad_new_from_template (ogm->srcpadtempl, "src"); - gst_pad_use_explicit_caps (ogm->srcpad); - if (!gst_pad_set_explicit_caps (ogm->srcpad, caps)) { - GST_ELEMENT_ERROR (ogm, CORE, NEGOTIATION, (NULL), (NULL)); - //gst_object_unref (GST_OBJECT (ogm->srcpad)); - ogm->srcpad = NULL; - break; - } + //gst_pad_use_explicit_caps (ogm->srcpad); + //if (!gst_pad_set_explicit_caps (ogm->srcpad, caps)) { + // GST_ELEMENT_ERROR (ogm, CORE, NEGOTIATION, (NULL), (NULL)); + //gst_object_unref (GST_OBJECT (ogm->srcpad)); + // ogm->srcpad = NULL; + // break; + //} gst_element_add_pad (GST_ELEMENT (ogm), ogm->srcpad); break; } @@ -637,8 +637,9 @@ gst_ogm_parse_chain (GstPad * pad, GstData * dat) case 'v':{ gint samples = (ogm->hdr.streamtype[0] == 'v') ? 1 : xsize; - if (keyframe) - GST_BUFFER_FLAG_SET (sbuf, GST_BUFFER_KEY_UNIT); + if (!keyframe) + GST_BUFFER_FLAG_SET (sbuf, GST_BUFFER_DELTA_UNIT); + GST_BUFFER_TIMESTAMP (sbuf) = (GST_SECOND / 10000000) * ogm->next_granulepos * ogm->hdr.time_unit; GST_BUFFER_DURATION (sbuf) = (GST_SECOND / 10000000) * @@ -659,8 +660,7 @@ gst_ogm_parse_chain (GstPad * pad, GstData * dat) GST_ELEMENT_ERROR (ogm, RESOURCE, SYNC, (NULL), (NULL)); break; } - if (sbuf) - gst_pad_push (ogm->srcpad, GST_DATA (sbuf)); + gst_pad_push (ogm->srcpad, sbuf); } else { GST_ELEMENT_ERROR (ogm, STREAM, WRONG_TYPE, ("Wrong packet startcode 0x%02x", data[0]), (NULL)); @@ -669,6 +669,8 @@ gst_ogm_parse_chain (GstPad * pad, GstData * dat) } gst_buffer_unref (buf); + + return GST_FLOW_OK; } static GstElementStateReturn diff --git a/ext/theora/theoradec.c b/ext/theora/theoradec.c index 315c977bcc..ec14a180e9 100644 --- a/ext/theora/theoradec.c +++ b/ext/theora/theoradec.c @@ -63,6 +63,8 @@ struct _GstTheoraDec gint offset_x, offset_y; gboolean crop; + + GList *queued; }; struct _GstTheoraDecClass @@ -109,7 +111,8 @@ static void theora_dec_get_property (GObject * object, guint prop_id, static void theora_dec_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); -static void theora_dec_chain (GstPad * pad, GstData * data); +static gboolean theora_dec_sink_event (GstPad * pad, GstEvent * event); +static GstFlowReturn theora_dec_chain (GstPad * pad, GstBuffer * buffer); static GstElementStateReturn theora_dec_change_state (GstElement * element); static gboolean theora_dec_src_event (GstPad * pad, GstEvent * event); static gboolean theora_dec_src_query (GstPad * pad, @@ -164,13 +167,13 @@ gst_theora_dec_init (GstTheoraDec * dec) (&theora_dec_sink_factory), "sink"); gst_pad_set_formats_function (dec->sinkpad, theora_get_formats); gst_pad_set_convert_function (dec->sinkpad, theora_dec_sink_convert); + gst_pad_set_event_function (dec->sinkpad, theora_dec_sink_event); gst_pad_set_chain_function (dec->sinkpad, theora_dec_chain); gst_element_add_pad (GST_ELEMENT (dec), dec->sinkpad); dec->srcpad = gst_pad_new_from_template (gst_static_pad_template_get (&theora_dec_src_factory), "src"); - gst_pad_use_explicit_caps (dec->srcpad); gst_pad_set_event_mask_function (dec->srcpad, theora_get_event_masks); gst_pad_set_event_function (dec->srcpad, theora_dec_src_event); gst_pad_set_query_type_function (dec->srcpad, theora_get_query_types); @@ -180,9 +183,8 @@ gst_theora_dec_init (GstTheoraDec * dec) gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad); - GST_FLAG_SET (dec, GST_ELEMENT_EVENT_AWARE); - dec->crop = THEORA_DEF_CROP; + dec->queued = NULL; } /* FIXME: copy from libtheora, theora should somehow make this available for seeking */ @@ -198,6 +200,27 @@ _theora_ilog (unsigned int v) return (ret); } +static void +_inc_granulepos (GstTheoraDec * dec) +{ + guint ilog; + gint framecount; + + if (dec->granulepos == -1) + return; + + ilog = _theora_ilog (dec->info.keyframe_frequency_force - 1); + + framecount = dec->granulepos >> ilog; + framecount += dec->granulepos - (framecount << ilog); + + GST_DEBUG_OBJECT (dec, "framecount=%d, ilog=%u", framecount, ilog); + + framecount++; + + dec->granulepos = (framecount << ilog); +} + static const GstFormat * theora_get_formats (GstPad * pad) { @@ -249,7 +272,7 @@ theora_dec_src_convert (GstPad * pad, GstTheoraDec *dec; guint64 scale = 1; - dec = GST_THEORA_DEC (gst_pad_get_parent (pad)); + dec = GST_THEORA_DEC (GST_PAD_PARENT (pad)); /* we need the info part before we can done something */ if (dec->packetno < 1) @@ -310,7 +333,7 @@ theora_dec_sink_convert (GstPad * pad, gboolean res = TRUE; GstTheoraDec *dec; - dec = GST_THEORA_DEC (gst_pad_get_parent (pad)); + dec = GST_THEORA_DEC (GST_PAD_PARENT (pad)); /* we need the info part before we can done something */ if (dec->packetno < 1) @@ -376,7 +399,7 @@ theora_dec_src_query (GstPad * pad, GstQueryType query, GstFormat * format, gint64 * value) { gint64 granulepos; - GstTheoraDec *dec = GST_THEORA_DEC (gst_pad_get_parent (pad)); + GstTheoraDec *dec = GST_THEORA_DEC (GST_PAD_PARENT (pad)); GstFormat my_format = GST_FORMAT_DEFAULT; guint64 time; @@ -385,9 +408,7 @@ theora_dec_src_query (GstPad * pad, GstQueryType query, GstFormat * format, granulepos = dec->granulepos; } else { /* for the total, we just forward the query to the peer */ - if (!gst_pad_query (GST_PAD_PEER (dec->sinkpad), query, &my_format, - &granulepos)) - return FALSE; + return gst_pad_query (GST_PAD_PEER (dec->sinkpad), query, format, value); } /* and convert to the final format in two steps with time as the @@ -402,6 +423,7 @@ theora_dec_src_query (GstPad * pad, GstQueryType query, GstFormat * format, GST_LOG_OBJECT (dec, "query %u: peer returned granulepos: %llu - we return %llu (format %u)", query, granulepos, *value, *format); + return TRUE; } @@ -412,7 +434,7 @@ theora_dec_src_event (GstPad * pad, GstEvent * event) GstTheoraDec *dec; GstFormat format; - dec = GST_THEORA_DEC (gst_pad_get_parent (pad)); + dec = GST_THEORA_DEC (GST_PAD_PARENT (pad)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEEK:{ @@ -455,31 +477,36 @@ theora_dec_src_event (GstPad * pad, GstEvent * event) return res; } -static void -theora_dec_event (GstTheoraDec * dec, GstEvent * event) +static gboolean +theora_dec_sink_event (GstPad * pad, GstEvent * event) { - guint64 value, time, bytes; + guint64 start_value, end_value, time, bytes; + GstTheoraDec *dec; + + dec = GST_THEORA_DEC (GST_PAD_PARENT (pad)); GST_LOG_OBJECT (dec, "handling event"); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_DISCONTINUOUS: - if (gst_event_discont_get_value (event, GST_FORMAT_DEFAULT, &value)) { - dec->granulepos = value; + if (gst_event_discont_get_value (event, GST_FORMAT_DEFAULT, + &start_value, &end_value)) { + dec->granulepos = start_value; GST_DEBUG_OBJECT (dec, "setting granuleposition to %" G_GUINT64_FORMAT " after discont", - value); + start_value); } else { GST_WARNING_OBJECT (dec, "discont event didn't include offset, we might set it wrong now"); + dec->granulepos = -1; } if (dec->packetno < 3) { if (dec->granulepos != 0) GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), ("can't handle discont before parsing first 3 packets")); dec->packetno = 0; - gst_pad_push (dec->srcpad, GST_DATA (gst_event_new_discontinuous (FALSE, - GST_FORMAT_TIME, (guint64) 0, GST_FORMAT_DEFAULT, - (guint64) 0, GST_FORMAT_BYTES, (guint64) 0, 0))); + gst_pad_push_event (dec->srcpad, gst_event_new_discontinuous (FALSE, + GST_FORMAT_TIME, (guint64) 0, GST_FORMAT_DEFAULT, + (guint64) 0, GST_FORMAT_BYTES, (guint64) 0, 0)); } else { GstFormat time_format, default_format, bytes_format; @@ -491,15 +518,15 @@ theora_dec_event (GstTheoraDec * dec, GstEvent * event) if (theora_dec_sink_convert (dec->sinkpad, GST_FORMAT_DEFAULT, dec->granulepos, &time_format, &time) && theora_dec_src_convert (dec->srcpad, GST_FORMAT_TIME, time, - &default_format, &value) + &default_format, &start_value) && theora_dec_src_convert (dec->srcpad, GST_FORMAT_TIME, time, &bytes_format, &bytes)) { - gst_pad_push (dec->srcpad, - GST_DATA (gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME, - time, GST_FORMAT_DEFAULT, value, GST_FORMAT_BYTES, bytes, - 0))); + gst_pad_push_event (dec->srcpad, + gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME, + time, GST_FORMAT_DEFAULT, start_value, GST_FORMAT_BYTES, + bytes, 0)); /* store new framenumber */ - dec->packetno = value + 3; + dec->packetno = start_value + 3; } else { GST_ERROR_OBJECT (dec, "failed to parse data for DISCONT event, not sending any"); @@ -511,38 +538,94 @@ theora_dec_event (GstTheoraDec * dec, GstEvent * event) default: break; } - gst_pad_event_default (dec->sinkpad, event); + return gst_pad_event_default (dec->sinkpad, event); } #define ROUND_UP_2(x) (((x) + 1) & ~1) #define ROUND_UP_4(x) (((x) + 3) & ~3) #define ROUND_UP_8(x) (((x) + 7) & ~7) -static void -theora_dec_chain (GstPad * pad, GstData * data) +static GstFlowReturn +theora_dec_chain (GstPad * pad, GstBuffer * buffer) { GstBuffer *buf; GstTheoraDec *dec; ogg_packet packet; guint64 offset_end; GstClockTime outtime; + GstFlowReturn result = GST_FLOW_OK; - dec = GST_THEORA_DEC (gst_pad_get_parent (pad)); - if (GST_IS_EVENT (data)) { - theora_dec_event (dec, GST_EVENT (data)); - return; - } + dec = GST_THEORA_DEC (GST_PAD_PARENT (pad)); - buf = GST_BUFFER (data); + buf = GST_BUFFER (buffer); if (dec->packetno >= 3) { - offset_end = GST_BUFFER_OFFSET_END (buf); - if (offset_end != -1) { - dec->granulepos = offset_end; - } + /* try timestamp first */ + outtime = GST_BUFFER_TIMESTAMP (buf); - /* granulepos to time */ - outtime = GST_SECOND * theora_granule_time (&dec->state, dec->granulepos); + if (outtime == -1) { + gboolean need_flush = FALSE; + + /* the offset end field in theora is actually the time of + * this frame, not the end time */ + offset_end = GST_BUFFER_OFFSET_END (buf); + GST_DEBUG_OBJECT (dec, "got buffer with granule %lld", offset_end); + + if (offset_end != -1) { + if (dec->granulepos == -1) { + GST_DEBUG_OBJECT (dec, "first buffer with granule"); + need_flush = TRUE; + } + dec->granulepos = offset_end; + } + if (dec->granulepos == -1) { + /* we need to wait for a buffer with at least a timestamp or an + * offset before we can generate valid timestamps */ + dec->queued = g_list_append (dec->queued, buf); + GST_DEBUG_OBJECT (dec, "queued buffer"); + return GST_FLOW_OK; + } else { + /* granulepos to time */ + outtime = + GST_SECOND * theora_granule_time (&dec->state, dec->granulepos); + + GST_DEBUG_OBJECT (dec, "granulepos=%lld outtime=%" GST_TIME_FORMAT, + dec->granulepos, GST_TIME_ARGS (outtime)); + + if (need_flush) { + GList *walk; + GstClockTime patch; + gint64 len; + gint64 old_granule = dec->granulepos; + + dec->granulepos = -1; + + len = g_list_length (dec->queued); + + GST_DEBUG_OBJECT (dec, "first buffer with granule, flushing"); + + /* now resubmit all queued buffers with corrected timestamps */ + for (walk = dec->queued; walk; walk = g_list_next (walk)) { + GstBuffer *qbuffer = GST_BUFFER (walk->data); + + patch = outtime - (GST_SECOND * len * dec->info.fps_denominator) / + dec->info.fps_numerator, + GST_DEBUG_OBJECT (dec, "patch buffer %lld %lld", len, patch); + GST_BUFFER_TIMESTAMP (qbuffer) = patch; + + /* buffers are unreffed in chain function */ + theora_dec_chain (pad, qbuffer); + len--; + } + g_list_free (dec->queued); + dec->queued = NULL; + + dec->granulepos = old_granule; + } + } + } else { + GST_DEBUG_OBJECT (dec, "got buffer with timestamp %lld", outtime); + } } else { /* we don't know yet */ outtime = -1; @@ -556,10 +639,8 @@ theora_dec_chain (GstPad * pad, GstData * data) packet.b_o_s = (packet.packetno == 0) ? 1 : 0; packet.e_o_s = 0; - GST_DEBUG_OBJECT (dec, "outtime=%" GST_TIME_FORMAT " (%" - G_GUINT64_FORMAT ") header=0x%02x packetno=%d", - GST_TIME_ARGS (outtime), outtime, - (gint) packet.packet[0], packet.packetno); + GST_DEBUG_OBJECT (dec, "header=%d packetno=%lld, outtime=%" GST_TIME_FORMAT, + packet.packet[0], packet.packetno, GST_TIME_ARGS (outtime)); /* switch depending on packet type */ if (packet.packet[0] & 0x80) { @@ -571,6 +652,7 @@ theora_dec_chain (GstPad * pad, GstData * data) if (theora_decode_header (&dec->info, &dec->comment, &packet)) { GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE, (NULL), ("couldn't read header packet")); + result = GST_FLOW_ERROR; goto done; } @@ -595,7 +677,7 @@ theora_dec_chain (GstPad * pad, GstData * data) GST_TAG_ENCODER_VERSION, dec->info.version_major, GST_TAG_NOMINAL_BITRATE, dec->info.target_bitrate, GST_TAG_VIDEO_CODEC, "Theora", NULL); - gst_element_found_tags_for_pad (GST_ELEMENT (dec), dec->srcpad, 0, list); + //gst_element_found_tags_for_pad (GST_ELEMENT (dec), dec->srcpad, 0, list); dec->packetno++; } else if (packet.packetno == 2) { @@ -657,8 +739,8 @@ theora_dec_chain (GstPad * pad, GstData * data) "pixel-aspect-ratio", GST_TYPE_FRACTION, par_num, par_den, "width", G_TYPE_INT, dec->width, "height", G_TYPE_INT, dec->height, NULL); - gst_pad_set_explicit_caps (dec->srcpad, caps); - gst_caps_free (caps); + gst_pad_set_caps (dec->srcpad, caps); + gst_caps_unref (caps); dec->initialized = TRUE; dec->packetno++; @@ -677,6 +759,7 @@ theora_dec_chain (GstPad * pad, GstData * data) dec->packetno++; if (!dec->initialized) { + result = GST_FLOW_ERROR; goto done; } @@ -684,30 +767,7 @@ theora_dec_chain (GstPad * pad, GstData * data) * for keyframes */ keyframe = (packet.packet[0] & 0x40) == 0; if (keyframe) { - guint ilog; - guint64 framecount; - gboolean add_one = FALSE; - dec->need_keyframe = FALSE; - ilog = _theora_ilog (dec->info.keyframe_frequency_force - 1); - if (dec->granulepos % (1 << ilog) == 0 && - dec->granulepos > 0 && GST_BUFFER_OFFSET_END (buf) == -1) { - dec->granulepos--; - add_one = TRUE; - } - framecount = dec->granulepos >> ilog; - framecount += dec->granulepos - (framecount << ilog); - if (add_one) { - framecount++; - } - dec->granulepos = framecount << ilog; - if (add_one) { - outtime = GST_SECOND * theora_granule_time (&dec->state, - dec->granulepos); - GST_DEBUG_OBJECT (dec, - "Correcting output time to %" GST_TIME_FORMAT, - GST_TIME_ARGS (outtime)); - } } else if (dec->need_keyframe) { GST_WARNING_OBJECT (dec, "dropping frame because we need a keyframe"); /* drop frames if we're looking for a keyframe */ @@ -716,16 +776,18 @@ theora_dec_chain (GstPad * pad, GstData * data) if (theora_decode_packetin (&dec->state, &packet)) { GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE, (NULL), ("theora decoder did not read data packet")); + result = GST_FLOW_ERROR; goto done; } if (theora_decode_YUVout (&dec->state, &yuv) < 0) { GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE, (NULL), ("couldn't read out YUV image")); + result = GST_FLOW_ERROR; goto done; } - g_return_if_fail (yuv.y_width == dec->info.width); - g_return_if_fail (yuv.y_height == dec->info.height); + g_return_val_if_fail (yuv.y_width == dec->info.width, GST_FLOW_ERROR); + g_return_val_if_fail (yuv.y_height == dec->info.height, GST_FLOW_ERROR); width = dec->width; height = dec->height; @@ -742,7 +804,10 @@ theora_dec_chain (GstPad * pad, GstData * data) /* now copy over the area contained in offset_x,offset_y, * frame_width, frame_height */ - out = gst_pad_alloc_buffer (dec->srcpad, GST_BUFFER_OFFSET_NONE, out_size); + out = gst_pad_alloc_buffer (dec->srcpad, GST_BUFFER_OFFSET_NONE, out_size, + GST_PAD_CAPS (dec->srcpad)); + if (out == NULL) + goto done; /* copy the visible region to the destination. This is actually pretty * complicated and gstreamer doesn't support all the needed caps to do this @@ -790,11 +855,13 @@ theora_dec_chain (GstPad * pad, GstData * data) dec->info.fps_numerator; GST_BUFFER_TIMESTAMP (out) = outtime; - gst_pad_push (dec->srcpad, GST_DATA (out)); + result = gst_pad_push (dec->srcpad, out); } done: - gst_data_unref (data); - dec->granulepos++; + gst_buffer_unref (buffer); + _inc_granulepos (dec); + + return result; } static GstElementStateReturn @@ -810,6 +877,7 @@ theora_dec_change_state (GstElement * element) theora_comment_init (&dec->comment); dec->need_keyframe = TRUE; dec->initialized = FALSE; + dec->granulepos = -1; break; case GST_STATE_PAUSED_TO_PLAYING: break; @@ -820,7 +888,7 @@ theora_dec_change_state (GstElement * element) theora_comment_clear (&dec->comment); theora_info_clear (&dec->info); dec->packetno = 0; - dec->granulepos = 0; + dec->granulepos = -1; break; case GST_STATE_READY_TO_NULL: break; diff --git a/ext/theora/theoraenc.c b/ext/theora/theoraenc.c index 24862e0077..84c5255294 100644 --- a/ext/theora/theoraenc.c +++ b/ext/theora/theoraenc.c @@ -103,7 +103,7 @@ struct _GstTheoraEnc guint packetno; guint64 bytes_out; - guint64 initial_delay; + guint64 next_ts; }; struct _GstTheoraEncClass @@ -170,10 +170,10 @@ GST_STATIC_PAD_TEMPLATE ("src", GST_BOILERPLATE (GstTheoraEnc, gst_theora_enc, GstElement, GST_TYPE_ELEMENT); -static void theora_enc_chain (GstPad * pad, GstData * data); +static gboolean theora_enc_sink_event (GstPad * pad, GstEvent * event); +static GstFlowReturn theora_enc_chain (GstPad * pad, GstBuffer * buffer); static GstElementStateReturn theora_enc_change_state (GstElement * element); -static GstPadLinkReturn theora_enc_sink_link (GstPad * pad, - const GstCaps * caps); +static gboolean theora_enc_sink_setcaps (GstPad * pad, GstCaps * caps); static void theora_enc_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); static void theora_enc_set_property (GObject * object, guint prop_id, @@ -255,13 +255,13 @@ gst_theora_enc_init (GstTheoraEnc * enc) gst_pad_new_from_template (gst_static_pad_template_get (&theora_enc_sink_factory), "sink"); gst_pad_set_chain_function (enc->sinkpad, theora_enc_chain); - gst_pad_set_link_function (enc->sinkpad, theora_enc_sink_link); + gst_pad_set_event_function (enc->sinkpad, theora_enc_sink_event); + gst_pad_set_setcaps_function (enc->sinkpad, theora_enc_sink_setcaps); gst_element_add_pad (GST_ELEMENT (enc), enc->sinkpad); enc->srcpad = gst_pad_new_from_template (gst_static_pad_template_get (&theora_enc_src_factory), "src"); - gst_pad_use_explicit_caps (enc->srcpad); gst_element_add_pad (GST_ELEMENT (enc), enc->srcpad); enc->center = THEORA_DEF_CENTER; @@ -276,12 +276,10 @@ gst_theora_enc_init (GstTheoraEnc * enc) enc->keyframe_threshold = THEORA_DEF_KEYFRAME_THRESHOLD; enc->keyframe_mindistance = THEORA_DEF_KEYFRAME_MINDISTANCE; enc->noise_sensitivity = THEORA_DEF_NOISE_SENSITIVITY; - - GST_FLAG_SET (enc, GST_ELEMENT_EVENT_AWARE); } -static GstPadLinkReturn -theora_enc_sink_link (GstPad * pad, const GstCaps * caps) +static gboolean +theora_enc_sink_setcaps (GstPad * pad, GstCaps * caps) { GstStructure *structure = gst_caps_get_structure (caps, 0); GstTheoraEnc *enc = GST_THEORA_ENC (gst_pad_get_parent (pad)); @@ -289,9 +287,6 @@ theora_enc_sink_link (GstPad * pad, const GstCaps * caps) GValue fps = { 0 }; GValue framerate = { 0 }; - if (!gst_caps_is_fixed (caps)) - return GST_PAD_LINK_DELAYED; - gst_structure_get_int (structure, "width", &enc->width); gst_structure_get_int (structure, "height", &enc->height); gst_structure_get_double (structure, "framerate", &enc->fps); @@ -352,7 +347,7 @@ theora_enc_sink_link (GstPad * pad, const GstCaps * caps) theora_encode_init (&enc->state, &enc->info); - return GST_PAD_LINK_OK; + return TRUE; } /* prepare a buffer for transmission by passing data through libtheora */ @@ -361,43 +356,25 @@ theora_buffer_from_packet (GstTheoraEnc * enc, ogg_packet * packet, GstClockTime timestamp, GstClockTime duration) { GstBuffer *buf; - guint64 granulepos_delta, timestamp_delta; - - /* if duration is 0, it's a header packet and should - * have granulepos 0 (so no delta regardless of delay) */ - if (duration == 0) { - granulepos_delta = 0; - timestamp_delta = 0; - } else { - granulepos_delta = enc->initial_delay * enc->fps / GST_SECOND; - timestamp_delta = enc->initial_delay; - } buf = gst_pad_alloc_buffer (enc->srcpad, - GST_BUFFER_OFFSET_NONE, packet->bytes); + GST_BUFFER_OFFSET_NONE, packet->bytes, GST_PAD_CAPS (enc->srcpad)); memcpy (GST_BUFFER_DATA (buf), packet->packet, packet->bytes); GST_BUFFER_OFFSET (buf) = enc->bytes_out; - GST_BUFFER_OFFSET_END (buf) = packet->granulepos + granulepos_delta; - GST_BUFFER_TIMESTAMP (buf) = timestamp + timestamp_delta; + GST_BUFFER_OFFSET_END (buf) = packet->granulepos; + GST_BUFFER_TIMESTAMP (buf) = timestamp; GST_BUFFER_DURATION (buf) = duration; /* the second most significant bit of the first data byte is cleared * for keyframes */ if ((packet->packet[0] & 0x40) == 0) { - GST_BUFFER_FLAG_SET (buf, GST_BUFFER_KEY_UNIT); GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_DELTA_UNIT); } else { - GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_KEY_UNIT); GST_BUFFER_FLAG_SET (buf, GST_BUFFER_DELTA_UNIT); } enc->packetno++; - GST_DEBUG ("encoded buffer of %d bytes. granulepos = %" G_GINT64_FORMAT - " + %" G_GINT64_FORMAT " = %" G_GINT64_FORMAT, GST_BUFFER_SIZE (buf), - packet->granulepos, granulepos_delta, - packet->granulepos + granulepos_delta); - return buf; } @@ -408,7 +385,7 @@ theora_push_buffer (GstTheoraEnc * enc, GstBuffer * buffer) enc->bytes_out += GST_BUFFER_SIZE (buffer); if (GST_PAD_IS_USABLE (enc->srcpad)) { - gst_pad_push (enc->srcpad, GST_DATA (buffer)); + gst_pad_push (enc->srcpad, buffer); } else { gst_buffer_unref (buffer); } @@ -455,51 +432,38 @@ theora_set_header_on_caps (GstCaps * caps, GstBuffer * buf1, g_value_unset (&list); } -static void -theora_enc_chain (GstPad * pad, GstData * data) +static gboolean +theora_enc_sink_event (GstPad * pad, GstEvent * event) +{ + GstTheoraEnc *enc; + ogg_packet op; + + enc = GST_THEORA_ENC (GST_PAD_PARENT (pad)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_EOS: + /* push last packet with eos flag */ + while (theora_encode_packetout (&enc->state, 1, &op)) { + GstClockTime out_time = + theora_granule_time (&enc->state, op.granulepos) * GST_SECOND; + theora_push_packet (enc, &op, out_time, GST_SECOND / enc->fps); + } + default: + return gst_pad_event_default (pad, event); + } +} + +static GstFlowReturn +theora_enc_chain (GstPad * pad, GstBuffer * buffer) { GstTheoraEnc *enc; ogg_packet op; GstBuffer *buf; GstClockTime in_time; - enc = GST_THEORA_ENC (gst_pad_get_parent (pad)); - if (GST_IS_EVENT (data)) { - switch (GST_EVENT_TYPE (data)) { - case GST_EVENT_DISCONTINUOUS: - { - guint64 val; + enc = GST_THEORA_ENC (GST_PAD_PARENT (pad)); - if (gst_event_discont_get_value (GST_EVENT (data), GST_FORMAT_TIME, - &val)) { - /* theora does not support discontinuities in the middle of - * a stream so we can't just increase the granulepos to reflect - * the new position, or can we? would that still be according - * to spec? */ - if (enc->bytes_out == 0) { - enc->initial_delay = val; - GST_DEBUG ("initial delay = %" G_GUINT64_FORMAT, val); - } else { - GST_DEBUG ("mid stream discont: val = %" G_GUINT64_FORMAT, val); - } - } - gst_pad_event_default (pad, GST_EVENT (data)); - return; - } - case GST_EVENT_EOS: - /* push last packet with eos flag */ - while (theora_encode_packetout (&enc->state, 1, &op)) { - GstClockTime out_time = - theora_granule_time (&enc->state, op.granulepos) * GST_SECOND; - theora_push_packet (enc, &op, out_time, GST_SECOND / enc->fps); - } - default: - gst_pad_event_default (pad, GST_EVENT (data)); - return; - } - } - - buf = GST_BUFFER (data); + buf = GST_BUFFER (buffer); in_time = GST_BUFFER_TIMESTAMP (buf); /* no packets written yet, setup headers */ @@ -531,7 +495,7 @@ theora_enc_chain (GstPad * pad, GstData * data) /* negotiate with these caps */ GST_DEBUG ("here are the caps: %" GST_PTR_FORMAT, caps); - gst_pad_try_set_caps (enc->srcpad, caps); + gst_pad_set_caps (enc->srcpad, caps); /* push out the header buffers */ theora_push_buffer (enc, buf1); @@ -590,7 +554,7 @@ theora_enc_chain (GstPad * pad, GstData * data) dst_uv_stride = enc->info_width / 2; newbuf = gst_pad_alloc_buffer (enc->srcpad, - GST_BUFFER_OFFSET_NONE, y_size * 3 / 2); + GST_BUFFER_OFFSET_NONE, y_size * 3 / 2, GST_PAD_CAPS (enc->srcpad)); dest_y = yuv.y = GST_BUFFER_DATA (newbuf); dest_u = yuv.u = yuv.y + y_size; @@ -691,6 +655,8 @@ theora_enc_chain (GstPad * pad, GstData * data) gst_buffer_unref (buf); } + + return GST_FLOW_OK; } static GstElementStateReturn @@ -705,7 +671,6 @@ theora_enc_change_state (GstElement * element) theora_info_init (&enc->info); theora_comment_init (&enc->comment); enc->packetno = 0; - enc->initial_delay = 0; break; case GST_STATE_PAUSED_TO_PLAYING: break; diff --git a/ext/vorbis/Makefile.am b/ext/vorbis/Makefile.am index 380be3114a..ecf15aae7e 100644 --- a/ext/vorbis/Makefile.am +++ b/ext/vorbis/Makefile.am @@ -1,10 +1,10 @@ plugin_LTLIBRARIES = libgstvorbis.la libgstvorbis_la_SOURCES = vorbis.c \ - vorbisdec.c vorbisenc.c oggvorbisenc.c vorbisparse.c + vorbisdec.c vorbisenc.c vorbisparse.c libgstvorbis_la_CFLAGS = $(GST_CFLAGS) $(VORBIS_CFLAGS) ## AM_PATH_VORBIS also sets VORBISENC_LIBS libgstvorbis_la_LIBADD = $(VORBIS_LIBS) $(VORBISENC_LIBS) $(VORBISFILE_LIBS) libgstvorbis_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -noinst_HEADERS = vorbisenc.h vorbisdec.h oggvorbisenc.h vorbisparse.h +noinst_HEADERS = vorbisenc.h vorbisdec.h vorbisparse.h diff --git a/ext/vorbis/oggvorbisenc.c b/ext/vorbis/oggvorbisenc.c deleted file mode 100644 index 09d6917e39..0000000000 --- a/ext/vorbis/oggvorbisenc.c +++ /dev/null @@ -1,1007 +0,0 @@ -/* GStreamer - * Copyright (C) <1999> Erik Walthinsen - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif -#include -#include -#include -#include - -#include -#include -#include "oggvorbisenc.h" - -static GstPadTemplate *gst_oggvorbisenc_src_template, - *gst_oggvorbisenc_sink_template; - -/* elementfactory information */ -GstElementDetails oggvorbisenc_details = { - "Ogg Vorbis encoder (deprecated)", - "Codec/Encoder/Audio", - "Encodes audio in Vorbis format for Ogg containers", - "Monty , " "Wim Taymans ", -}; - -/* OggVorbisEnc signals and args */ -enum -{ - /* FILL ME */ - LAST_SIGNAL -}; - -enum -{ - ARG_0, - ARG_MAX_BITRATE, - ARG_BITRATE, - ARG_MIN_BITRATE, - ARG_QUALITY, - ARG_SERIAL, - ARG_MANAGED, - ARG_LAST_MESSAGE -}; - -static const GstFormat * -gst_oggvorbisenc_get_formats (GstPad * pad) -{ - static const GstFormat src_formats[] = { - GST_FORMAT_BYTES, - GST_FORMAT_TIME, - 0 - }; - static const GstFormat sink_formats[] = { - GST_FORMAT_BYTES, - GST_FORMAT_DEFAULT, - GST_FORMAT_TIME, - 0 - }; - - return (GST_PAD_IS_SRC (pad) ? src_formats : sink_formats); -} - -#define MAX_BITRATE_DEFAULT -1 -#define BITRATE_DEFAULT -1 -#define MIN_BITRATE_DEFAULT -1 -#define QUALITY_DEFAULT 0.3 - -static void gst_oggvorbisenc_base_init (gpointer g_class); -static void gst_oggvorbisenc_class_init (OggVorbisEncClass * klass); -static void gst_oggvorbisenc_init (OggVorbisEnc * vorbisenc); - -static void gst_oggvorbisenc_chain (GstPad * pad, GstData * _data); -static gboolean gst_oggvorbisenc_setup (OggVorbisEnc * vorbisenc); - -static void gst_oggvorbisenc_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); -static void gst_oggvorbisenc_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static GstElementStateReturn gst_oggvorbisenc_change_state (GstElement * - element); - -static GstElementClass *parent_class = NULL; - -/*static guint gst_oggvorbisenc_signals[LAST_SIGNAL] = { 0 }; */ - -GType -oggvorbisenc_get_type (void) -{ - static GType oggvorbisenc_type = 0; - - if (!oggvorbisenc_type) { - static const GTypeInfo oggvorbisenc_info = { - sizeof (OggVorbisEncClass), - gst_oggvorbisenc_base_init, - NULL, - (GClassInitFunc) gst_oggvorbisenc_class_init, - NULL, - NULL, - sizeof (OggVorbisEnc), - 0, - (GInstanceInitFunc) gst_oggvorbisenc_init, - }; - static const GInterfaceInfo tag_setter_info = { - NULL, - NULL, - NULL - }; - - oggvorbisenc_type = - g_type_register_static (GST_TYPE_ELEMENT, "OggVorbisEnc", - &oggvorbisenc_info, 0); - - g_type_add_interface_static (oggvorbisenc_type, GST_TYPE_TAG_SETTER, - &tag_setter_info); - } - return oggvorbisenc_type; -} - -static GstCaps * -vorbis_caps_factory (void) -{ - return gst_caps_new_simple ("application/ogg", NULL); -} - -static GstCaps * -raw_caps_factory (void) -{ - return - gst_caps_new_simple ("audio/x-raw-int", - "endianness", G_TYPE_INT, G_BYTE_ORDER, - "signed", G_TYPE_BOOLEAN, TRUE, - "width", G_TYPE_INT, 16, - "depth", G_TYPE_INT, 16, - "rate", GST_TYPE_INT_RANGE, 8000, 50000, - "channels", GST_TYPE_INT_RANGE, 1, 2, NULL); -} - -static void -gst_oggvorbisenc_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - GstCaps *raw_caps, *vorbis_caps; - - raw_caps = raw_caps_factory (); - vorbis_caps = vorbis_caps_factory (); - - gst_oggvorbisenc_sink_template = gst_pad_template_new ("sink", GST_PAD_SINK, - GST_PAD_ALWAYS, raw_caps); - gst_oggvorbisenc_src_template = gst_pad_template_new ("src", GST_PAD_SRC, - GST_PAD_ALWAYS, vorbis_caps); - gst_element_class_add_pad_template (element_class, - gst_oggvorbisenc_sink_template); - gst_element_class_add_pad_template (element_class, - gst_oggvorbisenc_src_template); - gst_element_class_set_details (element_class, &oggvorbisenc_details); -} - -static void -gst_oggvorbisenc_class_init (OggVorbisEncClass * klass) -{ - GObjectClass *gobject_class; - GstElementClass *gstelement_class; - - gobject_class = (GObjectClass *) klass; - gstelement_class = (GstElementClass *) klass; - - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MAX_BITRATE, - g_param_spec_int ("max-bitrate", "Maximum Bitrate", - "Specify a maximum bitrate (in bps). Useful for encoding for a fixed-size channel", - -1, G_MAXINT, MAX_BITRATE_DEFAULT, G_PARAM_READWRITE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BITRATE, - g_param_spec_int ("bitrate", "Target Bitrate", - "Specify a target average bitrate (in bps). ", - -1, G_MAXINT, BITRATE_DEFAULT, G_PARAM_READWRITE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MIN_BITRATE, - g_param_spec_int ("min_bitrate", "Minimum Bitrate", - "Specify a minimum bitrate (in bps).", - -1, G_MAXINT, MIN_BITRATE_DEFAULT, G_PARAM_READWRITE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_QUALITY, - g_param_spec_float ("quality", "Quality", - "Specify quality instead of specifying a particular bitrate.", - 0.0, 1.0, QUALITY_DEFAULT, G_PARAM_READWRITE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SERIAL, - g_param_spec_int ("serial", "Serial", - "Specify a serial number for the stream. (-1 is random)", -1, - G_MAXINT, -1, G_PARAM_READWRITE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MANAGED, - g_param_spec_boolean ("managed", "Managed", - "Enable bitrate management engine", FALSE, G_PARAM_READWRITE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LAST_MESSAGE, - g_param_spec_string ("last-message", "last-message", - "The last status message", NULL, G_PARAM_READABLE)); - - parent_class = g_type_class_ref (GST_TYPE_ELEMENT); - - gobject_class->set_property = gst_oggvorbisenc_set_property; - gobject_class->get_property = gst_oggvorbisenc_get_property; - - gstelement_class->change_state = gst_oggvorbisenc_change_state; -} - -static GstPadLinkReturn -gst_oggvorbisenc_sinkconnect (GstPad * pad, const GstCaps * caps) -{ - OggVorbisEnc *vorbisenc; - GstStructure *structure; - - vorbisenc = GST_OGGVORBISENC (gst_pad_get_parent (pad)); - vorbisenc->setup = FALSE; - - structure = gst_caps_get_structure (caps, 0); - gst_structure_get_int (structure, "channels", &vorbisenc->channels); - gst_structure_get_int (structure, "rate", &vorbisenc->frequency); - - gst_oggvorbisenc_setup (vorbisenc); - - if (vorbisenc->setup) - return GST_PAD_LINK_OK; - - return GST_PAD_LINK_REFUSED; -} - -static gboolean -gst_oggvorbisenc_convert_src (GstPad * pad, GstFormat src_format, - gint64 src_value, GstFormat * dest_format, gint64 * dest_value) -{ - gboolean res = TRUE; - OggVorbisEnc *vorbisenc; - gint64 avg; - - vorbisenc = GST_OGGVORBISENC (gst_pad_get_parent (pad)); - - if (vorbisenc->samples_in == 0 || - vorbisenc->bytes_out == 0 || vorbisenc->frequency == 0) - return FALSE; - - avg = (vorbisenc->bytes_out * vorbisenc->frequency) / (vorbisenc->samples_in); - - switch (src_format) { - case GST_FORMAT_BYTES: - switch (*dest_format) { - case GST_FORMAT_TIME: - *dest_value = src_value * GST_SECOND / avg; - break; - default: - res = FALSE; - } - break; - case GST_FORMAT_TIME: - switch (*dest_format) { - case GST_FORMAT_BYTES: - *dest_value = src_value * avg / GST_SECOND; - break; - default: - res = FALSE; - } - break; - default: - res = FALSE; - } - return res; -} - -static gboolean -gst_oggvorbisenc_convert_sink (GstPad * pad, GstFormat src_format, - gint64 src_value, GstFormat * dest_format, gint64 * dest_value) -{ - gboolean res = TRUE; - guint scale = 1; - gint bytes_per_sample; - OggVorbisEnc *vorbisenc; - - vorbisenc = GST_OGGVORBISENC (gst_pad_get_parent (pad)); - - bytes_per_sample = vorbisenc->channels * 2; - - switch (src_format) { - case GST_FORMAT_BYTES: - switch (*dest_format) { - case GST_FORMAT_DEFAULT: - if (bytes_per_sample == 0) - return FALSE; - *dest_value = src_value / bytes_per_sample; - break; - case GST_FORMAT_TIME: - { - gint byterate = bytes_per_sample * vorbisenc->frequency; - - if (byterate == 0) - return FALSE; - *dest_value = src_value * GST_SECOND / byterate; - break; - } - default: - res = FALSE; - } - break; - case GST_FORMAT_DEFAULT: - switch (*dest_format) { - case GST_FORMAT_BYTES: - *dest_value = src_value * bytes_per_sample; - break; - case GST_FORMAT_TIME: - if (vorbisenc->frequency == 0) - return FALSE; - *dest_value = src_value * GST_SECOND / vorbisenc->frequency; - break; - default: - res = FALSE; - } - break; - case GST_FORMAT_TIME: - switch (*dest_format) { - case GST_FORMAT_BYTES: - scale = bytes_per_sample; - /* fallthrough */ - case GST_FORMAT_DEFAULT: - *dest_value = src_value * scale * vorbisenc->frequency / GST_SECOND; - break; - default: - res = FALSE; - } - break; - default: - res = FALSE; - } - return res; -} - -static const GstQueryType * -gst_oggvorbisenc_get_query_types (GstPad * pad) -{ - static const GstQueryType gst_oggvorbisenc_src_query_types[] = { - GST_QUERY_TOTAL, - GST_QUERY_POSITION, - 0 - }; - - return gst_oggvorbisenc_src_query_types; -} - -static gboolean -gst_oggvorbisenc_src_query (GstPad * pad, GstQueryType type, - GstFormat * format, gint64 * value) -{ - gboolean res = TRUE; - OggVorbisEnc *vorbisenc; - - vorbisenc = GST_OGGVORBISENC (gst_pad_get_parent (pad)); - - switch (type) { - case GST_QUERY_TOTAL: - { - switch (*format) { - case GST_FORMAT_BYTES: - case GST_FORMAT_TIME: - { - gint64 peer_value; - const GstFormat *peer_formats; - - res = FALSE; - - peer_formats = - gst_pad_get_formats (GST_PAD_PEER (vorbisenc->sinkpad)); - - while (peer_formats && *peer_formats && !res) { - - GstFormat peer_format = *peer_formats; - - /* do the probe */ - if (gst_pad_query (GST_PAD_PEER (vorbisenc->sinkpad), - GST_QUERY_TOTAL, &peer_format, &peer_value)) { - GstFormat conv_format; - - /* convert to TIME */ - conv_format = GST_FORMAT_TIME; - res = gst_pad_convert (vorbisenc->sinkpad, - peer_format, peer_value, &conv_format, value); - /* and to final format */ - res &= gst_pad_convert (pad, - GST_FORMAT_TIME, *value, format, value); - } - peer_formats++; - } - break; - } - default: - res = FALSE; - break; - } - break; - } - case GST_QUERY_POSITION: - switch (*format) { - default: - { - /* we only know about our samples, convert to requested format */ - res = gst_pad_convert (pad, - GST_FORMAT_BYTES, vorbisenc->bytes_out, format, value); - break; - } - } - break; - default: - res = FALSE; - break; - } - return res; -} - -static void -gst_oggvorbisenc_init (OggVorbisEnc * vorbisenc) -{ - vorbisenc->sinkpad = - gst_pad_new_from_template (gst_oggvorbisenc_sink_template, "sink"); - gst_element_add_pad (GST_ELEMENT (vorbisenc), vorbisenc->sinkpad); - gst_pad_set_chain_function (vorbisenc->sinkpad, gst_oggvorbisenc_chain); - gst_pad_set_link_function (vorbisenc->sinkpad, gst_oggvorbisenc_sinkconnect); - gst_pad_set_convert_function (vorbisenc->sinkpad, - GST_DEBUG_FUNCPTR (gst_oggvorbisenc_convert_sink)); - gst_pad_set_formats_function (vorbisenc->sinkpad, - GST_DEBUG_FUNCPTR (gst_oggvorbisenc_get_formats)); - - vorbisenc->srcpad = - gst_pad_new_from_template (gst_oggvorbisenc_src_template, "src"); - gst_pad_set_query_function (vorbisenc->srcpad, - GST_DEBUG_FUNCPTR (gst_oggvorbisenc_src_query)); - gst_pad_set_query_type_function (vorbisenc->srcpad, - GST_DEBUG_FUNCPTR (gst_oggvorbisenc_get_query_types)); - gst_pad_set_convert_function (vorbisenc->srcpad, - GST_DEBUG_FUNCPTR (gst_oggvorbisenc_convert_src)); - gst_pad_set_formats_function (vorbisenc->srcpad, - GST_DEBUG_FUNCPTR (gst_oggvorbisenc_get_formats)); - gst_element_add_pad (GST_ELEMENT (vorbisenc), vorbisenc->srcpad); - - vorbisenc->channels = -1; - vorbisenc->frequency = -1; - - vorbisenc->managed = FALSE; - vorbisenc->max_bitrate = MAX_BITRATE_DEFAULT; - vorbisenc->bitrate = BITRATE_DEFAULT; - vorbisenc->min_bitrate = MIN_BITRATE_DEFAULT; - vorbisenc->quality = QUALITY_DEFAULT; - vorbisenc->quality_set = FALSE; - vorbisenc->serial = -1; - vorbisenc->last_message = NULL; - - vorbisenc->setup = FALSE; - vorbisenc->eos = FALSE; - vorbisenc->header_sent = FALSE; - - vorbisenc->tags = gst_tag_list_new (); - - /* we're chained and we can deal with events */ - GST_FLAG_SET (vorbisenc, GST_ELEMENT_EVENT_AWARE); -} - - -static gchar * -gst_oggvorbisenc_get_tag_value (const GstTagList * list, const gchar * tag, - int index) -{ - gchar *vorbisvalue = NULL; - - if (tag == NULL) { - return NULL; - } - - /* get tag name right */ - if ((strcmp (tag, GST_TAG_TRACK_NUMBER) == 0) - || (strcmp (tag, GST_TAG_ALBUM_VOLUME_NUMBER) == 0) - || (strcmp (tag, GST_TAG_TRACK_COUNT) == 0) - || (strcmp (tag, GST_TAG_ALBUM_VOLUME_COUNT) == 0)) { - guint track_no; - - if (!gst_tag_list_get_uint_index (list, tag, index, &track_no)) - g_assert_not_reached (); - vorbisvalue = g_strdup_printf ("%u", track_no); - } else if (strcmp (tag, GST_TAG_DATE) == 0) { - /* FIXME: how are dates represented in vorbis files? */ - GDate *date; - guint u; - - if (!gst_tag_list_get_uint_index (list, tag, index, &u)) - g_assert_not_reached (); - date = g_date_new_julian (u); - vorbisvalue = - g_strdup_printf ("%04d-%02d-%02d", (gint) g_date_get_year (date), - (gint) g_date_get_month (date), (gint) g_date_get_day (date)); - g_date_free (date); - } else if (gst_tag_get_type (tag) == G_TYPE_STRING) { - if (!gst_tag_list_get_string_index (list, tag, index, &vorbisvalue)) - g_assert_not_reached (); - } - - return vorbisvalue; -} - -static void -gst_oggvorbisenc_metadata_set1 (const GstTagList * list, const gchar * tag, - gpointer vorbisenc) -{ - const gchar *vorbistag = NULL; - gchar *vorbisvalue = NULL; - guint i, count; - OggVorbisEnc *enc = GST_OGGVORBISENC (vorbisenc); - - vorbistag = gst_tag_to_vorbis_tag (tag); - if (vorbistag == NULL) { - return; - } - - count = gst_tag_list_get_tag_size (list, tag); - for (i = 0; i < count; i++) { - vorbisvalue = gst_oggvorbisenc_get_tag_value (list, tag, i); - - if (vorbisvalue != NULL) { - vorbis_comment_add_tag (&enc->vc, g_strdup (vorbistag), vorbisvalue); - } - } -} - -static void -gst_oggvorbisenc_set_metadata (OggVorbisEnc * vorbisenc) -{ - GstTagList *copy; - const GstTagList *user_tags; - - user_tags = gst_tag_setter_get_list (GST_TAG_SETTER (vorbisenc)); - if (!(vorbisenc->tags || user_tags)) - return; - - copy = - gst_tag_list_merge (user_tags, vorbisenc->tags, - gst_tag_setter_get_merge_mode (GST_TAG_SETTER (vorbisenc))); - vorbis_comment_init (&vorbisenc->vc); - gst_tag_list_foreach (copy, gst_oggvorbisenc_metadata_set1, vorbisenc); - gst_tag_list_free (copy); -} - -static gchar * -get_constraints_string (OggVorbisEnc * vorbisenc) -{ - gint min = vorbisenc->min_bitrate; - gint max = vorbisenc->max_bitrate; - gchar *result; - - if (min > 0 && max > 0) - result = g_strdup_printf ("(min %d bps, max %d bps)", min, max); - else if (min > 0) - result = g_strdup_printf ("(min %d bps, no max)", min); - else if (max > 0) - result = g_strdup_printf ("(no min, max %d bps)", max); - else - result = g_strdup_printf ("(no min or max)"); - - return result; -} - -static void -update_start_message (OggVorbisEnc * vorbisenc) -{ - gchar *constraints; - - g_free (vorbisenc->last_message); - - if (vorbisenc->bitrate > 0) { - if (vorbisenc->managed) { - constraints = get_constraints_string (vorbisenc); - vorbisenc->last_message = - g_strdup_printf ("encoding at average bitrate %d bps %s", - vorbisenc->bitrate, constraints); - g_free (constraints); - } else { - vorbisenc->last_message = - g_strdup_printf - ("encoding at approximate bitrate %d bps (VBR encoding enabled)", - vorbisenc->bitrate); - } - } else { - if (vorbisenc->quality_set) { - if (vorbisenc->managed) { - constraints = get_constraints_string (vorbisenc); - vorbisenc->last_message = - g_strdup_printf - ("encoding at quality level %2.2f using constrained VBR %s", - vorbisenc->quality, constraints); - g_free (constraints); - } else { - vorbisenc->last_message = - g_strdup_printf ("encoding at quality level %2.2f", - vorbisenc->quality); - } - } else { - constraints = get_constraints_string (vorbisenc); - vorbisenc->last_message = - g_strdup_printf ("encoding using bitrate management %s", constraints); - g_free (constraints); - } - } - - g_object_notify (G_OBJECT (vorbisenc), "last_message"); -} - -static gboolean -gst_oggvorbisenc_setup (OggVorbisEnc * vorbisenc) -{ - gint serial; - - if (vorbisenc->bitrate < 0 && vorbisenc->min_bitrate < 0 - && vorbisenc->max_bitrate < 0) { - vorbisenc->quality_set = TRUE; - } - - update_start_message (vorbisenc); - - /* choose an encoding mode */ - /* (mode 0: 44kHz stereo uncoupled, roughly 128kbps VBR) */ - vorbis_info_init (&vorbisenc->vi); - - if (vorbisenc->quality_set) { - if (vorbis_encode_setup_vbr (&vorbisenc->vi, - vorbisenc->channels, vorbisenc->frequency, vorbisenc->quality)) { - g_warning - ("vorbisenc: initialisation failed: invalid parameters for quality"); - vorbis_info_clear (&vorbisenc->vi); - return FALSE; - } - - /* do we have optional hard quality restrictions? */ - if (vorbisenc->max_bitrate > 0 || vorbisenc->min_bitrate > 0) { - struct ovectl_ratemanage_arg ai; - - vorbis_encode_ctl (&vorbisenc->vi, OV_ECTL_RATEMANAGE_GET, &ai); - - /* the bitrates used by libvorbisenc are in kbit/sec, ours in bit/sec - * also remember that in telecom kbit/sec is 1000 bit/sec */ - ai.bitrate_hard_min = vorbisenc->min_bitrate / 1000; - ai.bitrate_hard_max = vorbisenc->max_bitrate / 1000; - ai.management_active = 1; - - vorbis_encode_ctl (&vorbisenc->vi, OV_ECTL_RATEMANAGE_SET, &ai); - } - } else { - int ret; - - GST_LOG_OBJECT (vorbisenc, - "calling setup_managed with channels=%d, frequency=%d, bitrate=[%d,%d,%d]", - vorbisenc->channels, vorbisenc->frequency, - vorbisenc->min_bitrate > 0 ? vorbisenc->min_bitrate : -1, - vorbisenc->bitrate, - vorbisenc->max_bitrate > 0 ? vorbisenc->max_bitrate : -1); - ret = - vorbis_encode_setup_managed (&vorbisenc->vi, vorbisenc->channels, - vorbisenc->frequency, - vorbisenc->max_bitrate > 0 ? vorbisenc->max_bitrate : -1, - vorbisenc->bitrate, - vorbisenc->min_bitrate > 0 ? vorbisenc->min_bitrate : -1); - if (ret != 0) { - GST_ERROR_OBJECT (vorbisenc, - "vorbisenc: initialisation failed: invalid parameters for bitrate (returned: %d)", - ret); - vorbis_info_clear (&vorbisenc->vi); - return FALSE; - } - } - - if (vorbisenc->managed && vorbisenc->bitrate < 0) { - vorbis_encode_ctl (&vorbisenc->vi, OV_ECTL_RATEMANAGE_AVG, NULL); - } else if (!vorbisenc->managed) { - /* Turn off management entirely (if it was turned on). */ - vorbis_encode_ctl (&vorbisenc->vi, OV_ECTL_RATEMANAGE_SET, NULL); - } - vorbis_encode_setup_init (&vorbisenc->vi); - - /* set up the analysis state and auxiliary encoding storage */ - vorbis_analysis_init (&vorbisenc->vd, &vorbisenc->vi); - vorbis_block_init (&vorbisenc->vd, &vorbisenc->vb); - - /* set up our packet->stream encoder */ - /* pick a random serial number; that way we can more likely build - chained streams just by concatenation */ - if (vorbisenc->serial < 0) { - srand (time (NULL)); - serial = rand (); - } else { - serial = vorbisenc->serial; - } - - ogg_stream_init (&vorbisenc->os, serial); - - vorbisenc->setup = TRUE; - - return TRUE; -} - -static void -gst_oggvorbisenc_write_page (OggVorbisEnc * vorbisenc, ogg_page * page) -{ - GstBuffer *outbuf; - - outbuf = gst_buffer_new_and_alloc (page->header_len + page->body_len); - - memcpy (GST_BUFFER_DATA (outbuf), page->header, page->header_len); - memcpy (GST_BUFFER_DATA (outbuf) + page->header_len, - page->body, page->body_len); - - GST_DEBUG ("vorbisenc: encoded buffer of %d bytes", GST_BUFFER_SIZE (outbuf)); - - vorbisenc->bytes_out += GST_BUFFER_SIZE (outbuf); - - if (GST_PAD_IS_USABLE (vorbisenc->srcpad)) { - gst_pad_push (vorbisenc->srcpad, GST_DATA (outbuf)); - } else { - gst_buffer_unref (outbuf); - } -} - -static void -gst_oggvorbisenc_chain (GstPad * pad, GstData * _data) -{ - GstBuffer *buf = GST_BUFFER (_data); - OggVorbisEnc *vorbisenc; - - g_return_if_fail (pad != NULL); - g_return_if_fail (GST_IS_PAD (pad)); - g_return_if_fail (buf != NULL); - - vorbisenc = GST_OGGVORBISENC (gst_pad_get_parent (pad)); - - if (GST_IS_EVENT (buf)) { - GstEvent *event = GST_EVENT (buf); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_EOS: - /* end of file. this can be done implicitly in the mainline, - but it's easier to see here in non-clever fashion. - Tell the library we're at end of stream so that it can handle - the last frame and mark end of stream in the output properly */ - vorbis_analysis_wrote (&vorbisenc->vd, 0); - gst_event_unref (event); - break; - case GST_EVENT_TAG: - if (vorbisenc->tags) { - gst_tag_list_insert (vorbisenc->tags, gst_event_tag_get_list (event), - gst_tag_setter_get_merge_mode (GST_TAG_SETTER (vorbisenc))); - } else { - g_assert_not_reached (); - } - gst_pad_event_default (pad, event); - return; - default: - gst_pad_event_default (pad, event); - return; - } - } else { - gint16 *data; - gulong size; - gulong i, j; - float **buffer; - - if (!vorbisenc->setup) { - gst_buffer_unref (buf); - GST_ELEMENT_ERROR (vorbisenc, CORE, NEGOTIATION, (NULL), - ("encoder not initialized (input is not audio?)")); - return; - } - - if (!vorbisenc->header_sent) { - gint result; - - /* Vorbis streams begin with three headers; the initial header (with - most of the codec setup parameters) which is mandated by the Ogg - bitstream spec. The second header holds any comment fields. The - third header holds the bitstream codebook. We merely need to - make the headers, then pass them to libvorbis one at a time; - libvorbis handles the additional Ogg bitstream constraints */ - ogg_packet header; - ogg_packet header_comm; - ogg_packet header_code; - - gst_oggvorbisenc_set_metadata (vorbisenc); - vorbis_analysis_headerout (&vorbisenc->vd, &vorbisenc->vc, &header, - &header_comm, &header_code); - ogg_stream_packetin (&vorbisenc->os, &header); /* automatically placed in its own page */ - ogg_stream_packetin (&vorbisenc->os, &header_comm); - ogg_stream_packetin (&vorbisenc->os, &header_code); - - while ((result = ogg_stream_flush (&vorbisenc->os, &vorbisenc->og))) { - gst_oggvorbisenc_write_page (vorbisenc, &vorbisenc->og); - } - vorbisenc->header_sent = TRUE; - } - - /* data to encode */ - data = (gint16 *) GST_BUFFER_DATA (buf); - size = GST_BUFFER_SIZE (buf) / (vorbisenc->channels * 2); - - /* expose the buffer to submit data */ - buffer = vorbis_analysis_buffer (&vorbisenc->vd, size); - - /* uninterleave samples */ - for (i = 0; i < size; i++) { - for (j = 0; j < vorbisenc->channels; j++) { - buffer[j][i] = data[i * vorbisenc->channels + j] / 32768.f; - } - } - - /* tell the library how much we actually submitted */ - vorbis_analysis_wrote (&vorbisenc->vd, size); - - vorbisenc->samples_in += size; - - gst_buffer_unref (buf); - } - - /* vorbis does some data preanalysis, then divvies up blocks for - more involved (potentially parallel) processing. Get a single - block for encoding now */ - while (vorbis_analysis_blockout (&vorbisenc->vd, &vorbisenc->vb) == 1) { - - /* analysis */ - vorbis_analysis (&vorbisenc->vb, NULL); - vorbis_bitrate_addblock (&vorbisenc->vb); - - while (vorbis_bitrate_flushpacket (&vorbisenc->vd, &vorbisenc->op)) { - - /* weld the packet into the bitstream */ - ogg_stream_packetin (&vorbisenc->os, &vorbisenc->op); - - /* write out pages (if any) */ - while (!vorbisenc->eos) { - int result = ogg_stream_pageout (&vorbisenc->os, &vorbisenc->og); - - if (result == 0) - break; - - gst_oggvorbisenc_write_page (vorbisenc, &vorbisenc->og); - - /* this could be set above, but for illustrative purposes, I do - it here (to show that vorbis does know where the stream ends) */ - if (ogg_page_eos (&vorbisenc->og)) { - vorbisenc->eos = 1; - } - } - } - } - - if (vorbisenc->eos) { - /* clean up and exit. vorbis_info_clear() must be called last */ - ogg_stream_clear (&vorbisenc->os); - vorbis_block_clear (&vorbisenc->vb); - vorbis_dsp_clear (&vorbisenc->vd); - vorbis_info_clear (&vorbisenc->vi); - gst_pad_push (vorbisenc->srcpad, GST_DATA (gst_event_new (GST_EVENT_EOS))); - gst_element_set_eos (GST_ELEMENT (vorbisenc)); - } -} - -static void -gst_oggvorbisenc_get_property (GObject * object, guint prop_id, GValue * value, - GParamSpec * pspec) -{ - OggVorbisEnc *vorbisenc; - - /* it's not null if we got it, but it might not be ours */ - g_return_if_fail (GST_IS_OGGVORBISENC (object)); - - vorbisenc = GST_OGGVORBISENC (object); - - switch (prop_id) { - case ARG_MAX_BITRATE: - g_value_set_int (value, vorbisenc->max_bitrate); - break; - case ARG_BITRATE: - g_value_set_int (value, vorbisenc->bitrate); - break; - case ARG_MIN_BITRATE: - g_value_set_int (value, vorbisenc->min_bitrate); - break; - case ARG_QUALITY: - g_value_set_float (value, vorbisenc->quality); - break; - case ARG_SERIAL: - g_value_set_int (value, vorbisenc->serial); - break; - case ARG_MANAGED: - g_value_set_boolean (value, vorbisenc->managed); - break; - case ARG_LAST_MESSAGE: - g_value_set_string (value, vorbisenc->last_message); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_oggvorbisenc_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - OggVorbisEnc *vorbisenc; - - /* it's not null if we got it, but it might not be ours */ - g_return_if_fail (GST_IS_OGGVORBISENC (object)); - - vorbisenc = GST_OGGVORBISENC (object); - - switch (prop_id) { - case ARG_MAX_BITRATE: - { - gboolean old_value = vorbisenc->managed; - - vorbisenc->max_bitrate = g_value_get_int (value); - if (vorbisenc->min_bitrate > 0 && vorbisenc->max_bitrate > 0) - vorbisenc->managed = TRUE; - else - vorbisenc->managed = FALSE; - - if (old_value != vorbisenc->managed) - g_object_notify (object, "managed"); - break; - } - case ARG_BITRATE: - vorbisenc->bitrate = g_value_get_int (value); - break; - case ARG_MIN_BITRATE: - { - gboolean old_value = vorbisenc->managed; - - vorbisenc->min_bitrate = g_value_get_int (value); - if (vorbisenc->min_bitrate > 0 && vorbisenc->max_bitrate > 0) - vorbisenc->managed = TRUE; - else - vorbisenc->managed = FALSE; - - if (old_value != vorbisenc->managed) - g_object_notify (object, "managed"); - break; - } - case ARG_QUALITY: - vorbisenc->quality = g_value_get_float (value); - if (vorbisenc->quality >= 0.0) - vorbisenc->quality_set = TRUE; - else - vorbisenc->quality_set = FALSE; - break; - case ARG_SERIAL: - vorbisenc->serial = g_value_get_int (value); - break; - case ARG_MANAGED: - vorbisenc->managed = g_value_get_boolean (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static GstElementStateReturn -gst_oggvorbisenc_change_state (GstElement * element) -{ - OggVorbisEnc *vorbisenc = GST_OGGVORBISENC (element); - - switch (GST_STATE_TRANSITION (element)) { - case GST_STATE_NULL_TO_READY: - case GST_STATE_READY_TO_PAUSED: - vorbisenc->eos = FALSE; - break; - case GST_STATE_PAUSED_TO_PLAYING: - case GST_STATE_PLAYING_TO_PAUSED: - break; - case GST_STATE_PAUSED_TO_READY: - vorbisenc->setup = FALSE; - vorbisenc->header_sent = FALSE; - gst_tag_list_free (vorbisenc->tags); - vorbisenc->tags = gst_tag_list_new (); - break; - case GST_STATE_READY_TO_NULL: - default: - break; - } - - if (GST_ELEMENT_CLASS (parent_class)->change_state) - return GST_ELEMENT_CLASS (parent_class)->change_state (element); - - return GST_STATE_SUCCESS; -} diff --git a/ext/vorbis/oggvorbisenc.h b/ext/vorbis/oggvorbisenc.h deleted file mode 100644 index 850fafad7d..0000000000 --- a/ext/vorbis/oggvorbisenc.h +++ /dev/null @@ -1,100 +0,0 @@ -/* GStreamer - * Copyright (C) <1999> Erik Walthinsen - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - - -#ifndef __OGGVORBISENC_H__ -#define __OGGVORBISENC_H__ - - -#include - -#include - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#define GST_TYPE_OGGVORBISENC \ - (oggvorbisenc_get_type()) -#define GST_OGGVORBISENC(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OGGVORBISENC,OggVorbisEnc)) -#define GST_OGGVORBISENC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OGGVORBISENC,OggVorbisEncClass)) -#define GST_IS_OGGVORBISENC(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OGGVORBISENC)) -#define GST_IS_OGGVORBISENC_CLASS(obj) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OGGVORBISENC)) - -typedef struct _OggVorbisEnc OggVorbisEnc; -typedef struct _OggVorbisEncClass OggVorbisEncClass; - -struct _OggVorbisEnc { - GstElement element; - - GstPad *sinkpad, - *srcpad; - - ogg_stream_state os; /* take physical pages, weld into a logical - stream of packets */ - ogg_page og; /* one Ogg bitstream page. Vorbis packets are inside */ - ogg_packet op; /* one raw packet of data for decode */ - - vorbis_info vi; /* struct that stores all the static vorbis bitstream - settings */ - vorbis_comment vc; /* struct that stores all the user comments */ - - vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */ - vorbis_block vb; /* local working space for packet->PCM decode */ - - gboolean eos; - - gboolean managed; - gint bitrate; - gint min_bitrate; - gint max_bitrate; - gfloat quality; - gboolean quality_set; - gint serial; - - gint channels; - gint frequency; - - guint64 samples_in; - guint64 bytes_out; - - GstTagList * tags; - - gboolean setup; - gboolean header_sent; - gchar *last_message; -}; - -struct _OggVorbisEncClass { - GstElementClass parent_class; -}; - -GType oggvorbisenc_get_type(void); - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - - -#endif /* __OGGVORBISENC_H__ */ diff --git a/ext/vorbis/vorbis.c b/ext/vorbis/vorbis.c index dcf1524cc5..fab0856a87 100644 --- a/ext/vorbis/vorbis.c +++ b/ext/vorbis/vorbis.c @@ -22,7 +22,6 @@ #endif #include "vorbisenc.h" -#include "oggvorbisenc.h" #include "vorbisdec.h" #include "vorbisparse.h" @@ -33,15 +32,10 @@ GST_DEBUG_CATEGORY (vorbisparse_debug); static gboolean plugin_init (GstPlugin * plugin) { - if (!gst_library_load ("gstbytestream") || - !gst_library_load ("gstaudio") || !gst_library_load ("gsttags")) + if (!gst_library_load ("gstaudio") || !gst_library_load ("gsttags")) return FALSE; if (!gst_element_register (plugin, "vorbisenc", GST_RANK_NONE, - GST_TYPE_OGGVORBISENC)) - return FALSE; - - if (!gst_element_register (plugin, "rawvorbisenc", GST_RANK_NONE, GST_TYPE_VORBISENC)) return FALSE; diff --git a/ext/vorbis/vorbisdec.c b/ext/vorbis/vorbisdec.c index ca9759be56..5242d75e76 100644 --- a/ext/vorbis/vorbisdec.c +++ b/ext/vorbis/vorbisdec.c @@ -72,7 +72,8 @@ GST_STATIC_PAD_TEMPLATE ("sink", GST_BOILERPLATE (GstVorbisDec, gst_vorbis_dec, GstElement, GST_TYPE_ELEMENT); -static void vorbis_dec_chain (GstPad * pad, GstData * data); +static gboolean vorbis_dec_sink_event (GstPad * pad, GstEvent * event); +static GstFlowReturn vorbis_dec_chain (GstPad * pad, GstBuffer * buffer); static GstElementStateReturn vorbis_dec_change_state (GstElement * element); static const GstFormat *vorbis_dec_get_formats (GstPad * pad); @@ -152,6 +153,7 @@ gst_vorbis_dec_init (GstVorbisDec * dec) dec->sinkpad = gst_pad_new_from_template (gst_static_pad_template_get (&vorbis_dec_sink_factory), "sink"); + gst_pad_set_event_function (dec->sinkpad, vorbis_dec_sink_event); gst_pad_set_chain_function (dec->sinkpad, vorbis_dec_chain); gst_pad_set_formats_function (dec->sinkpad, vorbis_dec_get_formats); gst_pad_set_convert_function (dec->sinkpad, vorbis_dec_convert); @@ -160,7 +162,6 @@ gst_vorbis_dec_init (GstVorbisDec * dec) dec->srcpad = gst_pad_new_from_template (gst_static_pad_template_get (&vorbis_dec_src_factory), "src"); - gst_pad_use_explicit_caps (dec->srcpad); gst_pad_set_event_mask_function (dec->srcpad, vorbis_get_event_masks); gst_pad_set_event_function (dec->srcpad, vorbis_dec_src_event); gst_pad_set_query_type_function (dec->srcpad, vorbis_get_query_types); @@ -168,8 +169,6 @@ gst_vorbis_dec_init (GstVorbisDec * dec) gst_pad_set_formats_function (dec->srcpad, vorbis_dec_get_formats); gst_pad_set_convert_function (dec->srcpad, vorbis_dec_convert); gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad); - - GST_FLAG_SET (dec, GST_ELEMENT_EVENT_AWARE); } static gboolean @@ -240,16 +239,12 @@ vorbis_dec_src_query (GstPad * pad, GstQueryType query, GstFormat * format, { gint64 granulepos = 0; GstVorbisDec *dec = GST_VORBIS_DEC (gst_pad_get_parent (pad)); - GstFormat my_format = GST_FORMAT_DEFAULT; if (query == GST_QUERY_POSITION) { granulepos = dec->granulepos; } else { /* query peer in default format */ - if (!dec->sinkpad || !GST_PAD_PEER (dec->sinkpad) || - !gst_pad_query (GST_PAD_PEER (dec->sinkpad), query, &my_format, - &granulepos)) - return FALSE; + return gst_pad_query (GST_PAD_PEER (dec->sinkpad), query, format, value); } /* and convert to the final format */ @@ -257,7 +252,7 @@ vorbis_dec_src_query (GstPad * pad, GstQueryType query, GstFormat * format, return FALSE; GST_LOG_OBJECT (dec, - "query %u: peer returned granulepos: %llu - we return %llu (format %u)", + "query %u: peer returned granulepos: %llu - we return %llu (format %u)\n", query, granulepos, *value, *format); return TRUE; } @@ -271,16 +266,15 @@ vorbis_dec_src_event (GstPad * pad, GstEvent * event) switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEEK:{ guint64 value; - GstFormat my_format = GST_FORMAT_DEFAULT; + GstFormat my_format = GST_FORMAT_TIME; - /* convert to granulepos */ - res = vorbis_dec_convert (pad, GST_EVENT_SEEK_FORMAT (event), + /* convert to time */ + res = gst_pad_convert (pad, GST_EVENT_SEEK_FORMAT (event), GST_EVENT_SEEK_OFFSET (event), &my_format, &value); if (res) { GstEvent *real_seek = gst_event_new_seek ( (GST_EVENT_SEEK_TYPE (event) & ~GST_SEEK_FORMAT_MASK) | - GST_FORMAT_DEFAULT, - value); + GST_FORMAT_TIME, value); res = gst_pad_send_event (GST_PAD_PEER (dec->sinkpad), real_seek); } @@ -295,20 +289,24 @@ vorbis_dec_src_event (GstPad * pad, GstEvent * event) return res; } -static void -vorbis_dec_event (GstVorbisDec * dec, GstEvent * event) +static gboolean +vorbis_dec_sink_event (GstPad * pad, GstEvent * event) { - guint64 value, time, bytes; + guint64 start_value, end_value, time, bytes; + gboolean ret = TRUE; + GstVorbisDec *dec; + + dec = GST_VORBIS_DEC (GST_PAD_PARENT (pad)); GST_LOG_OBJECT (dec, "handling event"); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_DISCONTINUOUS: if (gst_event_discont_get_value (event, GST_FORMAT_DEFAULT, - (gint64 *) & value)) { - dec->granulepos = value; + (gint64 *) & start_value, &end_value)) { + dec->granulepos = start_value; GST_DEBUG_OBJECT (dec, "setting granuleposition to %" G_GUINT64_FORMAT " after discont", - value); + start_value); } else { GST_WARNING_OBJECT (dec, "discont event didn't include offset, we might set it wrong now"); @@ -318,9 +316,9 @@ vorbis_dec_event (GstVorbisDec * dec, GstEvent * event) GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), ("can't handle discont before parsing first 3 packets")); dec->packetno = 0; - gst_pad_push (dec->srcpad, GST_DATA (gst_event_new_discontinuous (FALSE, - GST_FORMAT_TIME, (guint64) 0, GST_FORMAT_DEFAULT, - (guint64) 0, GST_FORMAT_BYTES, (guint64) 0, 0))); + gst_pad_push_event (dec->srcpad, gst_event_new_discontinuous (FALSE, + GST_FORMAT_TIME, (guint64) 0, GST_FORMAT_DEFAULT, + (guint64) 0, GST_FORMAT_BYTES, (guint64) 0, 0)); } else { GstFormat time_format, default_format, bytes_format; @@ -334,10 +332,10 @@ vorbis_dec_event (GstVorbisDec * dec, GstEvent * event) dec->granulepos, &time_format, &time) && vorbis_dec_convert (dec->srcpad, GST_FORMAT_DEFAULT, dec->granulepos, &bytes_format, &bytes)) { - gst_pad_push (dec->srcpad, - GST_DATA (gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME, - time, GST_FORMAT_DEFAULT, dec->granulepos, - GST_FORMAT_BYTES, bytes, 0))); + gst_pad_push_event (dec->srcpad, + gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME, + time, GST_FORMAT_DEFAULT, dec->granulepos, + GST_FORMAT_BYTES, bytes, 0)); } else { GST_ERROR_OBJECT (dec, "failed to parse data for DISCONT event, not sending any"); @@ -349,25 +347,22 @@ vorbis_dec_event (GstVorbisDec * dec, GstEvent * event) gst_data_unref (GST_DATA (event)); break; default: - gst_pad_event_default (dec->sinkpad, event); + ret = gst_pad_event_default (dec->sinkpad, event); break; } + return ret; } -static void -vorbis_dec_chain (GstPad * pad, GstData * data) +static GstFlowReturn +vorbis_dec_chain (GstPad * pad, GstBuffer * buffer) { - GstBuffer *buf; + GstBuffer *buf = GST_BUFFER (buffer); GstVorbisDec *vd; - ogg_packet packet; /* lol */ + ogg_packet packet; + GstFlowReturn result = GST_FLOW_OK; - vd = GST_VORBIS_DEC (gst_pad_get_parent (pad)); - if (GST_IS_EVENT (data)) { - vorbis_dec_event (vd, GST_EVENT (data)); - return; - } + vd = GST_VORBIS_DEC (GST_PAD_PARENT (pad)); - buf = GST_BUFFER (data); /* make ogg_packet out of the buffer */ packet.packet = GST_BUFFER_DATA (buf); packet.bytes = GST_BUFFER_SIZE (buf); @@ -391,16 +386,16 @@ vorbis_dec_chain (GstPad * pad, GstData * data) GST_WARNING_OBJECT (GST_ELEMENT (vd), "unexpected packet type %d, expected %d", (gint) packet.packet[0], (gint) packet.packetno); - gst_data_unref (data); - return; + gst_buffer_unref (buffer); + return GST_FLOW_UNEXPECTED; } /* Packetno = 0 if the first byte is exactly 0x01 */ packet.b_o_s = (packet.packet[0] == 0x1) ? 1 : 0; if (vorbis_synthesis_headerin (&vd->vi, &vd->vc, &packet)) { GST_ELEMENT_ERROR (GST_ELEMENT (vd), STREAM, DECODE, (NULL), ("couldn't read header packet")); - gst_data_unref (data); - return; + gst_buffer_unref (buffer); + return GST_FLOW_ERROR; } if (packet.packetno == 1) { gchar *encoder = NULL; @@ -429,7 +424,7 @@ vorbis_dec_chain (GstPad * pad, GstData * data) if (vd->vi.bitrate_lower > 0) gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_MINIMUM_BITRATE, (guint) vd->vi.bitrate_lower, NULL); - gst_element_found_tags_for_pad (GST_ELEMENT (vd), vd->srcpad, 0, list); + //gst_element_found_tags_for_pad (GST_ELEMENT (vd), vd->srcpad, 0, list); } else if (packet.packetno == 2) { GstCaps *caps; const GstAudioChannelPosition *pos = NULL; @@ -490,20 +485,17 @@ vorbis_dec_chain (GstPad * pad, GstData * data) break; } default: - gst_data_unref (data); - gst_caps_free (caps); + gst_buffer_unref (buffer); + gst_caps_unref (caps); GST_ELEMENT_ERROR (vd, STREAM, NOT_IMPLEMENTED, (NULL), ("Unsupported channel count %d", vd->vi.channels)); - return; + return GST_FLOW_ERROR; } if (pos) { gst_audio_set_channel_positions (gst_caps_get_structure (caps, 0), pos); } - if (!gst_pad_set_explicit_caps (vd->srcpad, caps)) { - gst_caps_free (caps); - return; - } - gst_caps_free (caps); + gst_pad_set_caps (vd->srcpad, caps); + gst_caps_unref (caps); } } else { float **pcm; @@ -512,62 +504,71 @@ vorbis_dec_chain (GstPad * pad, GstData * data) if (packet.packetno < 3) { GST_ELEMENT_ERROR (GST_ELEMENT (vd), STREAM, DECODE, (NULL), ("no header sent yet (packet no is %d)", packet.packetno)); - gst_data_unref (data); - return; + gst_buffer_unref (buffer); + return GST_FLOW_ERROR; } /* normal data packet */ if (vorbis_synthesis (&vd->vb, &packet)) { GST_ELEMENT_ERROR (GST_ELEMENT (vd), STREAM, DECODE, (NULL), ("couldn't read data packet")); - gst_data_unref (data); - return; + gst_buffer_unref (buffer); + return GST_FLOW_ERROR; } if (vorbis_synthesis_blockin (&vd->vd, &vd->vb) < 0) { GST_ELEMENT_ERROR (GST_ELEMENT (vd), STREAM, DECODE, (NULL), ("vorbis decoder did not accept data packet")); - gst_data_unref (data); - return; + gst_buffer_unref (buffer); + return GST_FLOW_ERROR; } sample_count = vorbis_synthesis_pcmout (&vd->vd, &pcm); if (sample_count > 0) { int i, j; GstBuffer *out = gst_pad_alloc_buffer (vd->srcpad, GST_BUFFER_OFFSET_NONE, - sample_count * vd->vi.channels * sizeof (float)); - float *out_data = (float *) GST_BUFFER_DATA (out); + sample_count * vd->vi.channels * sizeof (float), + GST_PAD_CAPS (vd->srcpad)); + float *out_data; + + if (out != NULL) { + out_data = (float *) GST_BUFFER_DATA (out); #ifdef GST_VORBIS_DEC_SEQUENTIAL - for (i = 0; i < vd->vi.channels; i++) { - memcpy (out_data, pcm[i], sample_count * sizeof (float)); - out_data += sample_count; - } -#else - for (j = 0; j < sample_count; j++) { for (i = 0; i < vd->vi.channels; i++) { - *out_data = pcm[i][j]; - out_data++; + memcpy (out_data, pcm[i], sample_count * sizeof (float)); + out_data += sample_count; + } +#else + for (j = 0; j < sample_count; j++) { + for (i = 0; i < vd->vi.channels; i++) { + *out_data = pcm[i][j]; + out_data++; + } } - } #endif - GST_BUFFER_OFFSET (out) = vd->granulepos; - GST_BUFFER_OFFSET_END (out) = vd->granulepos + sample_count; - GST_BUFFER_TIMESTAMP (out) = vd->granulepos * GST_SECOND / vd->vi.rate; - GST_BUFFER_DURATION (out) = sample_count * GST_SECOND / vd->vi.rate; - GST_DEBUG ("Pushing data of time %" GST_TIME_FORMAT, - GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (out))); - gst_pad_push (vd->srcpad, GST_DATA (out)); + GST_BUFFER_OFFSET (out) = vd->granulepos; + GST_BUFFER_OFFSET_END (out) = vd->granulepos + sample_count; + GST_BUFFER_TIMESTAMP (out) = vd->granulepos * GST_SECOND / vd->vi.rate; + GST_BUFFER_DURATION (out) = sample_count * GST_SECOND / vd->vi.rate; + result = gst_pad_push (vd->srcpad, out); + vd->granulepos += sample_count; + } vorbis_synthesis_read (&vd->vd, sample_count); - vd->granulepos += sample_count; } } - gst_data_unref (data); + gst_buffer_unref (buffer); + + return result; } static GstElementStateReturn vorbis_dec_change_state (GstElement * element) { GstVorbisDec *vd = GST_VORBIS_DEC (element); + GstElementState transition; + GstElementStateReturn res; - switch (GST_STATE_TRANSITION (element)) { + transition = GST_STATE_TRANSITION (element); + + switch (transition) { case GST_STATE_NULL_TO_READY: break; case GST_STATE_READY_TO_PAUSED: @@ -576,6 +577,13 @@ vorbis_dec_change_state (GstElement * element) break; case GST_STATE_PAUSED_TO_PLAYING: break; + default: + break; + } + + res = parent_class->change_state (element); + + switch (transition) { case GST_STATE_PLAYING_TO_PAUSED: break; case GST_STATE_PAUSED_TO_READY: @@ -592,5 +600,5 @@ vorbis_dec_change_state (GstElement * element) break; } - return parent_class->change_state (element); + return res; } diff --git a/ext/vorbis/vorbisenc.c b/ext/vorbis/vorbisenc.c index 027dd1dae8..c0286e5bcc 100644 --- a/ext/vorbis/vorbisenc.c +++ b/ext/vorbis/vorbisenc.c @@ -103,7 +103,8 @@ static void gst_vorbisenc_base_init (gpointer g_class); static void gst_vorbisenc_class_init (VorbisEncClass * klass); static void gst_vorbisenc_init (VorbisEnc * vorbisenc); -static void gst_vorbisenc_chain (GstPad * pad, GstData * _data); +static gboolean gst_vorbisenc_sink_event (GstPad * pad, GstEvent * event); +static GstFlowReturn gst_vorbisenc_chain (GstPad * pad, GstBuffer * buffer); static gboolean gst_vorbisenc_setup (VorbisEnc * vorbisenc); static void gst_vorbisenc_get_property (GObject * object, guint prop_id, @@ -197,6 +198,9 @@ gst_vorbisenc_class_init (VorbisEncClass * klass) gobject_class = (GObjectClass *) klass; gstelement_class = (GstElementClass *) klass; + gobject_class->set_property = gst_vorbisenc_set_property; + gobject_class->get_property = gst_vorbisenc_get_property; + g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MAX_BITRATE, g_param_spec_int ("max-bitrate", "Maximum Bitrate", "Specify a maximum bitrate (in bps). Useful for streaming " @@ -226,14 +230,11 @@ gst_vorbisenc_class_init (VorbisEncClass * klass) parent_class = g_type_class_ref (GST_TYPE_ELEMENT); - gobject_class->set_property = gst_vorbisenc_set_property; - gobject_class->get_property = gst_vorbisenc_get_property; - gstelement_class->change_state = gst_vorbisenc_change_state; } -static GstPadLinkReturn -gst_vorbisenc_sinkconnect (GstPad * pad, const GstCaps * caps) +static gboolean +gst_vorbisenc_sink_setcaps (GstPad * pad, GstCaps * caps) { VorbisEnc *vorbisenc; GstStructure *structure; @@ -248,9 +249,9 @@ gst_vorbisenc_sinkconnect (GstPad * pad, const GstCaps * caps) gst_vorbisenc_setup (vorbisenc); if (vorbisenc->setup) - return GST_PAD_LINK_OK; + return TRUE; - return GST_PAD_LINK_REFUSED; + return FALSE; } static gboolean @@ -447,8 +448,9 @@ gst_vorbisenc_init (VorbisEnc * vorbisenc) vorbisenc->sinkpad = gst_pad_new_from_template (gst_vorbisenc_sink_template, "sink"); gst_element_add_pad (GST_ELEMENT (vorbisenc), vorbisenc->sinkpad); + gst_pad_set_event_function (vorbisenc->sinkpad, gst_vorbisenc_sink_event); gst_pad_set_chain_function (vorbisenc->sinkpad, gst_vorbisenc_chain); - gst_pad_set_link_function (vorbisenc->sinkpad, gst_vorbisenc_sinkconnect); + gst_pad_set_setcaps_function (vorbisenc->sinkpad, gst_vorbisenc_sink_setcaps); gst_pad_set_convert_function (vorbisenc->sinkpad, GST_DEBUG_FUNCPTR (gst_vorbisenc_convert_sink)); gst_pad_set_formats_function (vorbisenc->sinkpad, @@ -482,11 +484,6 @@ gst_vorbisenc_init (VorbisEnc * vorbisenc) vorbisenc->header_sent = FALSE; vorbisenc->tags = gst_tag_list_new (); - - vorbisenc->initial_delay = 0; - - /* we're chained and we can deal with events */ - GST_FLAG_SET (vorbisenc, GST_ELEMENT_EVENT_AWARE); } @@ -718,32 +715,16 @@ static GstBuffer * gst_vorbisenc_buffer_from_packet (VorbisEnc * vorbisenc, ogg_packet * packet) { GstBuffer *outbuf; - guint64 granulepos_delta, timestamp_delta; - - /* header packets always need to have granulepos 0, - * regardless of the initial delay */ - if (packet->granulepos == 0) { - granulepos_delta = 0; - timestamp_delta = 0; - } else { - granulepos_delta = - vorbisenc->initial_delay * vorbisenc->frequency / GST_SECOND; - timestamp_delta = vorbisenc->initial_delay; - } outbuf = gst_buffer_new_and_alloc (packet->bytes); memcpy (GST_BUFFER_DATA (outbuf), packet->packet, packet->bytes); GST_BUFFER_OFFSET (outbuf) = vorbisenc->bytes_out; - GST_BUFFER_OFFSET_END (outbuf) = packet->granulepos + granulepos_delta; + GST_BUFFER_OFFSET_END (outbuf) = packet->granulepos; GST_BUFFER_TIMESTAMP (outbuf) = vorbis_granule_time_copy (&vorbisenc->vd, - packet->granulepos) * GST_SECOND + timestamp_delta; - - GST_DEBUG ("encoded buffer of %d bytes. granulepos = %" G_GINT64_FORMAT - " + %" G_GINT64_FORMAT " = %" G_GINT64_FORMAT, GST_BUFFER_SIZE (outbuf), - packet->granulepos, granulepos_delta, - packet->granulepos + granulepos_delta); + packet->granulepos) * GST_SECOND; + GST_DEBUG ("encoded buffer of %d bytes", GST_BUFFER_SIZE (outbuf)); return outbuf; } @@ -754,7 +735,7 @@ gst_vorbisenc_push_buffer (VorbisEnc * vorbisenc, GstBuffer * buffer) vorbisenc->bytes_out += GST_BUFFER_SIZE (buffer); if (GST_PAD_IS_USABLE (vorbisenc->srcpad)) { - gst_pad_push (vorbisenc->srcpad, GST_DATA (buffer)); + gst_pad_push (vorbisenc->srcpad, buffer); } else { gst_buffer_unref (buffer); } @@ -800,63 +781,49 @@ gst_vorbisenc_set_header_on_caps (GstCaps * caps, GstBuffer * buf1, g_value_unset (&list); } -static void -gst_vorbisenc_chain (GstPad * pad, GstData * _data) +static gboolean +gst_vorbisenc_sink_event (GstPad * pad, GstEvent * event) { - GstBuffer *buf = GST_BUFFER (_data); + gboolean res = TRUE; VorbisEnc *vorbisenc; - g_return_if_fail (pad != NULL); - g_return_if_fail (GST_IS_PAD (pad)); - g_return_if_fail (buf != NULL); + vorbisenc = GST_VORBISENC (GST_PAD_PARENT (pad)); - vorbisenc = GST_VORBISENC (gst_pad_get_parent (pad)); - - if (GST_IS_EVENT (buf)) { - GstEvent *event = GST_EVENT (buf); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_EOS: - /* end of file. this can be done implicitly in the mainline, - but it's easier to see here in non-clever fashion. - Tell the library we're at end of stream so that it can handle - the last frame and mark end of stream in the output properly */ - vorbis_analysis_wrote (&vorbisenc->vd, 0); - vorbisenc->eos = TRUE; - gst_event_unref (event); - break; - case GST_EVENT_TAG: - if (vorbisenc->tags) { - gst_tag_list_insert (vorbisenc->tags, gst_event_tag_get_list (event), - gst_tag_setter_get_merge_mode (GST_TAG_SETTER (vorbisenc))); - } else { - g_assert_not_reached (); - } - gst_pad_event_default (pad, event); - return; - case GST_EVENT_DISCONTINUOUS: - { - guint64 val; - - if (gst_event_discont_get_value (event, GST_FORMAT_TIME, &val)) { - /* vorbis does not support discontinuities in the middle of - * a stream so we can't just increase the granulepos to reflect - * the new position, or can we? would that still be according - * to spec? */ - if (vorbisenc->samples_in == 0) { - vorbisenc->initial_delay = val; - GST_DEBUG ("initial delay = %" G_GUINT64_FORMAT, val); - } else { - GST_DEBUG ("mid stream discont: val = %" G_GUINT64_FORMAT, val); - } - } + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_EOS: + /* end of file. this can be done implicitly in the mainline, + but it's easier to see here in non-clever fashion. + Tell the library we're at end of stream so that it can handle + the last frame and mark end of stream in the output properly */ + vorbis_analysis_wrote (&vorbisenc->vd, 0); + vorbisenc->eos = TRUE; + gst_event_unref (event); + break; + case GST_EVENT_TAG: + if (vorbisenc->tags) { + gst_tag_list_insert (vorbisenc->tags, gst_event_tag_get_list (event), + gst_tag_setter_get_merge_mode (GST_TAG_SETTER (vorbisenc))); + } else { + g_assert_not_reached (); } - /* fall through */ - default: - gst_pad_event_default (pad, event); - return; - } - } else { + res = gst_pad_event_default (pad, event); + break; + default: + res = gst_pad_event_default (pad, event); + break; + } + return res; +} + +static GstFlowReturn +gst_vorbisenc_chain (GstPad * pad, GstBuffer * buffer) +{ + GstBuffer *buf = GST_BUFFER (buffer); + VorbisEnc *vorbisenc; + + vorbisenc = GST_VORBISENC (GST_PAD_PARENT (pad)); + + { gfloat *data; gulong size; gulong i, j; @@ -866,7 +833,7 @@ gst_vorbisenc_chain (GstPad * pad, GstData * _data) gst_buffer_unref (buf); GST_ELEMENT_ERROR (vorbisenc, CORE, NEGOTIATION, (NULL), ("encoder not initialized (input is not audio?)")); - return; + return GST_FLOW_UNEXPECTED; } if (!vorbisenc->header_sent) { @@ -899,7 +866,7 @@ gst_vorbisenc_chain (GstPad * pad, GstData * _data) /* negotiate with these caps */ GST_DEBUG ("here are the caps: %" GST_PTR_FORMAT, caps); - gst_pad_try_set_caps (vorbisenc->srcpad, caps); + gst_pad_set_caps (vorbisenc->srcpad, caps); /* push out buffers */ gst_vorbisenc_push_buffer (vorbisenc, buf1); @@ -951,9 +918,10 @@ gst_vorbisenc_chain (GstPad * pad, GstData * _data) vorbis_block_clear (&vorbisenc->vb); vorbis_dsp_clear (&vorbisenc->vd); vorbis_info_clear (&vorbisenc->vi); - gst_pad_push (vorbisenc->srcpad, GST_DATA (gst_event_new (GST_EVENT_EOS))); - gst_element_set_eos (GST_ELEMENT (vorbisenc)); + gst_pad_push_event (vorbisenc->srcpad, gst_event_new (GST_EVENT_EOS)); + //gst_element_set_eos (GST_ELEMENT (vorbisenc)); } + return GST_FLOW_OK; } static void @@ -1069,23 +1037,29 @@ static GstElementStateReturn gst_vorbisenc_change_state (GstElement * element) { VorbisEnc *vorbisenc = GST_VORBISENC (element); + GstElementState transition; + GstElementStateReturn res; - switch (GST_STATE_TRANSITION (element)) { + transition = GST_STATE_TRANSITION (element); + + switch (transition) { case GST_STATE_NULL_TO_READY: case GST_STATE_READY_TO_PAUSED: vorbisenc->eos = FALSE; break; case GST_STATE_PAUSED_TO_PLAYING: + default: + break; + } + + res = GST_ELEMENT_CLASS (parent_class)->change_state (element); + + switch (transition) { case GST_STATE_PLAYING_TO_PAUSED: break; case GST_STATE_PAUSED_TO_READY: vorbisenc->setup = FALSE; vorbisenc->header_sent = FALSE; - vorbisenc->initial_delay = 0; - vorbisenc->samples_in = 0; - vorbisenc->bytes_out = 0; - vorbisenc->channels = -1; - vorbisenc->frequency = -1; gst_tag_list_free (vorbisenc->tags); vorbisenc->tags = gst_tag_list_new (); break; @@ -1094,8 +1068,5 @@ gst_vorbisenc_change_state (GstElement * element) break; } - if (GST_ELEMENT_CLASS (parent_class)->change_state) - return GST_ELEMENT_CLASS (parent_class)->change_state (element); - - return GST_STATE_SUCCESS; + return res; } diff --git a/ext/vorbis/vorbisenc.h b/ext/vorbis/vorbisenc.h index 8bd13b9814..c81b14261a 100644 --- a/ext/vorbis/vorbisenc.h +++ b/ext/vorbis/vorbisenc.h @@ -77,8 +77,6 @@ struct _VorbisEnc { gboolean setup; gboolean header_sent; gchar *last_message; - - guint64 initial_delay; /* for streams not starting at timestamp/sample 0 */ }; struct _VorbisEncClass { diff --git a/ext/vorbis/vorbisparse.c b/ext/vorbis/vorbisparse.c index 95e6970f7d..bbcea816cb 100644 --- a/ext/vorbis/vorbisparse.c +++ b/ext/vorbis/vorbisparse.c @@ -62,7 +62,7 @@ GST_STATIC_PAD_TEMPLATE ("src", GST_BOILERPLATE (GstVorbisParse, gst_vorbis_parse, GstElement, GST_TYPE_ELEMENT); -static void vorbis_parse_chain (GstPad * pad, GstData * data); +static GstFlowReturn vorbis_parse_chain (GstPad * pad, GstBuffer * buffer); static GstElementStateReturn vorbis_parse_change_state (GstElement * element); static void @@ -143,23 +143,22 @@ vorbis_parse_set_header_on_caps (GstVorbisParse * parse, GstCaps * caps) g_value_unset (&list); } -static void -vorbis_parse_chain (GstPad * pad, GstData * data) +static GstFlowReturn +vorbis_parse_chain (GstPad * pad, GstBuffer * buffer) { GstBuffer *buf; GstVorbisParse *parse; - parse = GST_VORBIS_PARSE (gst_pad_get_parent (pad)); - g_assert (parse); + parse = GST_VORBIS_PARSE (GST_PAD_PARENT (pad)); - buf = GST_BUFFER (data); + buf = GST_BUFFER (buffer); parse->packetno++; /* if 1 <= packetno <= 3, it's streamheader, * so put it on the streamheader list and return */ if (parse->packetno <= 3) { parse->streamheader = g_list_append (parse->streamheader, buf); - return; + return GST_FLOW_OK; } /* else, if we haven't sent streamheader buffers yet, @@ -172,7 +171,7 @@ vorbis_parse_chain (GstPad * pad, GstData * data) /* negotiate with these caps */ GST_DEBUG ("here are the caps: %" GST_PTR_FORMAT, caps); - gst_pad_try_set_caps (parse->srcpad, caps); + gst_pad_set_caps (parse->srcpad, caps); /* push out buffers */ gst_pad_push (parse->srcpad, parse->streamheader->data); @@ -182,7 +181,9 @@ vorbis_parse_chain (GstPad * pad, GstData * data) parse->streamheader_sent = TRUE; } /* just send on buffer by default */ - gst_pad_push (parse->srcpad, data); + gst_pad_push (parse->srcpad, buf); + + return GST_FLOW_OK; } static GstElementStateReturn diff --git a/gst-libs/gst/audio/audioclock.c b/gst-libs/gst/audio/audioclock.c index 65d2694f28..d60c92ecfe 100644 --- a/gst-libs/gst/audio/audioclock.c +++ b/gst-libs/gst/audio/audioclock.c @@ -30,7 +30,7 @@ static void gst_audio_clock_class_init (GstAudioClockClass * klass); static void gst_audio_clock_init (GstAudioClock * clock); static GstClockTime gst_audio_clock_get_internal_time (GstClock * clock); -static GstClockEntryStatus gst_audio_clock_id_wait_async (GstClock * clock, +static GstClockReturn gst_audio_clock_id_wait_async (GstClock * clock, GstClockEntry * entry); static void gst_audio_clock_id_unschedule (GstClock * clock, GstClockEntry * entry); @@ -184,7 +184,7 @@ compare_clock_entries (GstClockEntry * entry1, GstClockEntry * entry2) return entry1->time - entry2->time; } -static GstClockEntryStatus +static GstClockReturn gst_audio_clock_id_wait_async (GstClock * clock, GstClockEntry * entry) { GstAudioClock *aclock = (GstAudioClock *) clock; diff --git a/gst-libs/gst/audio/gstaudiofilter.c b/gst-libs/gst/audio/gstaudiofilter.c index 70ae6bf985..c5ba9e35bb 100644 --- a/gst-libs/gst/audio/gstaudiofilter.c +++ b/gst-libs/gst/audio/gstaudiofilter.c @@ -51,7 +51,7 @@ static void gst_audiofilter_set_property (GObject * object, guint prop_id, static void gst_audiofilter_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); -static void gst_audiofilter_chain (GstPad * pad, GstData * _data); +static GstFlowReturn gst_audiofilter_chain (GstPad * pad, GstBuffer * buffer); GstCaps *gst_audiofilter_class_get_capslist (GstAudiofilterClass * klass); static GstElementClass *parent_class = NULL; @@ -113,18 +113,20 @@ gst_audiofilter_class_init (gpointer g_class, gpointer class_data) } static GstPadLinkReturn -gst_audiofilter_link (GstPad * pad, const GstCaps * caps) +gst_audiofilter_link (GstPad * pad, GstPad * peer) { GstAudiofilter *audiofilter; - GstPadLinkReturn ret; - GstPadLinkReturn link_ret; - GstStructure *structure; + + //GstPadLinkReturn ret; + //GstPadLinkReturn link_ret; + //GstStructure *structure; GstAudiofilterClass *audiofilter_class; GST_DEBUG ("gst_audiofilter_link"); audiofilter = GST_AUDIOFILTER (gst_pad_get_parent (pad)); audiofilter_class = GST_AUDIOFILTER_CLASS (G_OBJECT_GET_CLASS (audiofilter)); +#if 0 ret = GST_PAD_LINK_DELAYED; /* intialise with dummy value */ if (pad == audiofilter->srcpad) { link_ret = gst_pad_try_set_caps (audiofilter->sinkpad, caps); @@ -158,6 +160,7 @@ gst_audiofilter_link (GstPad * pad, const GstCaps * caps) if (audiofilter_class->setup) (audiofilter_class->setup) (audiofilter); +#endif return GST_PAD_LINK_OK; } @@ -177,7 +180,7 @@ gst_audiofilter_init (GTypeInstance * instance, gpointer g_class) gst_element_add_pad (GST_ELEMENT (audiofilter), audiofilter->sinkpad); gst_pad_set_chain_function (audiofilter->sinkpad, gst_audiofilter_chain); gst_pad_set_link_function (audiofilter->sinkpad, gst_audiofilter_link); - gst_pad_set_getcaps_function (audiofilter->sinkpad, gst_pad_proxy_getcaps); + //gst_pad_set_getcaps_function (audiofilter->sinkpad, gst_pad_proxy_getcaps); pad_template = gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src"); @@ -185,24 +188,24 @@ gst_audiofilter_init (GTypeInstance * instance, gpointer g_class) audiofilter->srcpad = gst_pad_new_from_template (pad_template, "src"); gst_element_add_pad (GST_ELEMENT (audiofilter), audiofilter->srcpad); gst_pad_set_link_function (audiofilter->srcpad, gst_audiofilter_link); - gst_pad_set_getcaps_function (audiofilter->srcpad, gst_pad_proxy_getcaps); + //gst_pad_set_getcaps_function (audiofilter->srcpad, gst_pad_proxy_getcaps); audiofilter->inited = FALSE; } -static void -gst_audiofilter_chain (GstPad * pad, GstData * data) +static GstFlowReturn +gst_audiofilter_chain (GstPad * pad, GstBuffer * buffer) { - GstBuffer *inbuf = GST_BUFFER (data); + GstBuffer *inbuf = GST_BUFFER (buffer); GstAudiofilter *audiofilter; GstBuffer *outbuf; GstAudiofilterClass *audiofilter_class; GST_DEBUG ("gst_audiofilter_chain"); - g_return_if_fail (pad != NULL); - g_return_if_fail (GST_IS_PAD (pad)); - g_return_if_fail (inbuf != NULL); + g_return_val_if_fail (pad != NULL, GST_FLOW_ERROR); + g_return_val_if_fail (GST_IS_PAD (pad), GST_FLOW_ERROR); + g_return_val_if_fail (inbuf != NULL, GST_FLOW_ERROR); audiofilter = GST_AUDIOFILTER (gst_pad_get_parent (pad)); //g_return_if_fail (audiofilter->inited); @@ -212,14 +215,14 @@ gst_audiofilter_chain (GstPad * pad, GstData * data) GST_BUFFER_SIZE (inbuf), GST_OBJECT_NAME (audiofilter)); if (audiofilter->passthru) { - gst_pad_push (audiofilter->srcpad, data); - return; + gst_pad_push (audiofilter->srcpad, buffer); + return GST_FLOW_OK; } audiofilter->size = GST_BUFFER_SIZE (inbuf); audiofilter->n_samples = audiofilter->size / audiofilter->bytes_per_sample; - if (gst_data_is_writable (data)) { + if (gst_data_is_writable (GST_DATA (buffer))) { if (audiofilter_class->filter_inplace) { (audiofilter_class->filter_inplace) (audiofilter, inbuf); outbuf = inbuf; @@ -246,7 +249,9 @@ gst_audiofilter_chain (GstPad * pad, GstData * data) gst_buffer_unref (inbuf); } - gst_pad_push (audiofilter->srcpad, GST_DATA (outbuf)); + gst_pad_push (audiofilter->srcpad, outbuf); + + return GST_FLOW_OK; } static void diff --git a/gst-libs/gst/audio/testchannels.c b/gst-libs/gst/audio/testchannels.c index b886c820a3..3f226f318c 100644 --- a/gst-libs/gst/audio/testchannels.c +++ b/gst-libs/gst/audio/testchannels.c @@ -49,7 +49,7 @@ main (gint argc, gchar * argv[]) str = gst_caps_to_string (caps); g_print ("Test caps #2: %s\n", str); g_free (str); - gst_caps_free (caps); + gst_caps_unref (caps); return 0; } diff --git a/gst-libs/gst/gconf/gconf.c b/gst-libs/gst/gconf/gconf.c index de3182f0b1..d7318524e3 100644 --- a/gst-libs/gst/gconf/gconf.c +++ b/gst-libs/gst/gconf/gconf.c @@ -55,25 +55,34 @@ gst_bin_find_unconnected_pad (GstBin * bin, GstPadDirection direction) const GList *pads = NULL; GstElement *element = NULL; - elements = (GList *) gst_bin_get_list (bin); + GST_LOCK (bin); + elements = bin->children; /* traverse all elements looking for unconnected pads */ while (elements && pad == NULL) { element = GST_ELEMENT (elements->data); - pads = gst_element_get_pad_list (element); + GST_LOCK (element); + pads = element->pads; while (pads) { + GstPad *testpad = GST_PAD (pads->data); + /* check if the direction matches */ - if (GST_PAD_DIRECTION (GST_PAD (pads->data)) == direction) { - if (GST_PAD_PEER (GST_PAD (pads->data)) == NULL) { + if (GST_PAD_DIRECTION (testpad) == direction) { + GST_LOCK (pad); + if (GST_PAD_PEER (testpad) == NULL) { + GST_UNLOCK (pad); /* found it ! */ - pad = GST_PAD (pads->data); + pad = testpad; + break; } + GST_UNLOCK (pad); } - if (pad) - break; /* found one already */ pads = g_list_next (pads); } + GST_UNLOCK (element); elements = g_list_next (elements); } + GST_UNLOCK (bin); + return pad; } diff --git a/gst-libs/gst/media-info/media-info-priv.c b/gst-libs/gst/media-info/media-info-priv.c index 27fadde5ea..37f29a8447 100644 --- a/gst-libs/gst/media-info/media-info-priv.c +++ b/gst-libs/gst/media-info/media-info-priv.c @@ -273,7 +273,7 @@ G_STMT_START { \ #define CAPS_RESET(target) \ G_STMT_START { \ - if (target) gst_caps_free (target); \ + if (target) gst_caps_unref (target); \ target = NULL; \ } G_STMT_END CAPS_RESET (priv->type); @@ -509,7 +509,9 @@ gmip_find_type (GstMediaInfoPriv * priv, GError ** error) if (!gmip_find_type_pre (priv, error)) return FALSE; GST_DEBUG ("gmip_find_type: iterating"); - while ((priv->type == NULL) && gst_bin_iterate (GST_BIN (priv->pipeline))) + while ((priv->type == NULL) + // && gst_bin_iterate (GST_BIN (priv->pipeline)) + ) GMI_DEBUG ("+"); GMI_DEBUG ("\n"); return gmip_find_type_post (priv); @@ -616,7 +618,7 @@ gmip_find_stream (GstMediaInfoPriv * priv) /* iterate until caps are found */ /* FIXME: this should be done through the plugin sending some signal * that it is ready for queries */ - while (gst_bin_iterate (GST_BIN (priv->pipeline)) && priv->format == NULL); + //while (gst_bin_iterate (GST_BIN (priv->pipeline)) && priv->format == NULL); if (gst_element_set_state (priv->pipeline, GST_STATE_PAUSED) == GST_STATE_FAILURE) g_warning ("Couldn't set to paused"); @@ -659,7 +661,9 @@ gmip_find_track_metadata (GstMediaInfoPriv * priv) { gmip_find_track_metadata_pre (priv); GST_DEBUG ("gmip_find_metadata: iterating"); - while ((priv->metadata == NULL) && gst_bin_iterate (GST_BIN (priv->pipeline))) + while ((priv->metadata == NULL) + //&& gst_bin_iterate (GST_BIN (priv->pipeline)) + ) GMI_DEBUG ("+"); GMI_DEBUG ("\n"); gmip_find_track_metadata_post (priv); @@ -729,8 +733,9 @@ gmip_find_track_streaminfo (GstMediaInfoPriv * priv) { gmip_find_track_streaminfo_pre (priv); GST_DEBUG ("DEBUG: gmip_find_streaminfo: iterating"); - while ((priv->streaminfo == NULL) && - gst_bin_iterate (GST_BIN (priv->pipeline))) + while ((priv->streaminfo == NULL) + // && gst_bin_iterate (GST_BIN (priv->pipeline)) + ) GMI_DEBUG ("+"); GMI_DEBUG ("\n"); gmip_find_track_streaminfo_post (priv); @@ -766,7 +771,9 @@ gmip_find_track_format (GstMediaInfoPriv * priv) { gmip_find_track_format_pre (priv); GST_DEBUG ("DEBUG: gmip_find_format: iterating"); - while ((priv->format == NULL) && gst_bin_iterate (GST_BIN (priv->pipeline))) + while ((priv->format == NULL) + //&& gst_bin_iterate (GST_BIN (priv->pipeline)) + ) GMI_DEBUG ("+"); GMI_DEBUG ("\n"); gmip_find_track_format_post (priv); diff --git a/gst-libs/gst/media-info/media-info.c b/gst-libs/gst/media-info/media-info.c index 16a6625029..8e1ecee43c 100644 --- a/gst-libs/gst/media-info/media-info.c +++ b/gst-libs/gst/media-info/media-info.c @@ -266,7 +266,8 @@ gst_media_info_read_idler (GstMediaInfo * info, GstMediaInfoStream ** streamp, gchar *mime; GST_LOG ("STATE_TYPEFIND"); - if ((priv->type == NULL) && gst_bin_iterate (GST_BIN (priv->pipeline))) { + //if ((priv->type == NULL) && gst_bin_iterate (GST_BIN (priv->pipeline))) { + if ((priv->type == NULL)) { GST_DEBUG ("iterating while in STATE_TYPEFIND"); GMI_DEBUG ("?"); return TRUE; @@ -298,7 +299,8 @@ gst_media_info_read_idler (GstMediaInfo * info, GstMediaInfoStream ** streamp, case GST_MEDIA_INFO_STATE_STREAM: { GST_LOG ("STATE_STREAM"); - if ((priv->format == NULL) && gst_bin_iterate (GST_BIN (priv->pipeline))) { + //if ((priv->format == NULL) && gst_bin_iterate (GST_BIN (priv->pipeline))) { + if ((priv->format == NULL)) { GMI_DEBUG ("?"); return TRUE; } @@ -317,7 +319,7 @@ gst_media_info_read_idler (GstMediaInfo * info, GstMediaInfoStream ** streamp, case GST_MEDIA_INFO_STATE_METADATA: { if ((priv->metadata == NULL) && - gst_bin_iterate (GST_BIN (priv->pipeline)) && + //gst_bin_iterate (GST_BIN (priv->pipeline)) && priv->metadata_iters < MAX_METADATA_ITERS) { GMI_DEBUG ("?"); priv->metadata_iters++; @@ -338,8 +340,9 @@ gst_media_info_read_idler (GstMediaInfo * info, GstMediaInfoStream ** streamp, } case GST_MEDIA_INFO_STATE_STREAMINFO: { - if ((priv->streaminfo == NULL) && - gst_bin_iterate (GST_BIN (priv->pipeline))) { + if ((priv->streaminfo == NULL) + //&& gst_bin_iterate (GST_BIN (priv->pipeline)) + ) { GMI_DEBUG ("?"); return TRUE; } @@ -355,7 +358,9 @@ gst_media_info_read_idler (GstMediaInfo * info, GstMediaInfoStream ** streamp, } case GST_MEDIA_INFO_STATE_FORMAT: { - if ((priv->format == NULL) && gst_bin_iterate (GST_BIN (priv->pipeline))) { + if ((priv->format == NULL) + // && gst_bin_iterate (GST_BIN (priv->pipeline)) + ) { GMI_DEBUG ("?"); return TRUE; } diff --git a/gst-libs/gst/play/play.c b/gst-libs/gst/play/play.c index d41c69a845..0e12ab5303 100644 --- a/gst-libs/gst/play/play.c +++ b/gst-libs/gst/play/play.c @@ -572,7 +572,8 @@ gst_play_get_sink_element (GstPlay * play, return element; } - elements = (GList *) gst_bin_get_list (GST_BIN (element)); + /* FIXME, not MT safe */ + elements = (GList *) GST_BIN (element)->children; /* traverse all elements looking for one without src pad */ @@ -588,7 +589,8 @@ gst_play_get_sink_element (GstPlay * play, if (GST_IS_ELEMENT (element)) return element; } else { - pads = gst_element_get_pad_list (element); + /* FIXME, not MT safe */ + pads = element->pads; has_src = FALSE; has_correct_type = FALSE; while (pads) { @@ -631,7 +633,7 @@ gst_play_get_sink_element (GstPlay * play, } } - gst_caps_free (caps); + gst_caps_unref (caps); switch (sink_type) { case GST_PLAY_SINK_TYPE_AUDIO: @@ -678,6 +680,8 @@ gst_play_get_sink_element (GstPlay * play, * Returns all elements that are used by @play implementing the given interface. * * Returns: a #GList of #GstElement implementing the interface. + * + * Not MT safe. */ GList * @@ -685,16 +689,24 @@ gst_play_get_all_by_interface (GstPlay * play, GType interface_type) { GstElement *videosink = NULL, *audiosink = NULL; GList *res = NULL; + GstIterator *it = NULL; g_object_get (G_OBJECT (play->priv->playbin), "video-sink", &videosink, "audio-sink", &audiosink, NULL); /* ehw... */ if (videosink && GST_IS_BIN (videosink)) { - res = gst_bin_get_all_by_interface (GST_BIN (videosink), interface_type); + it = gst_bin_iterate_all_by_interface (GST_BIN (videosink), interface_type); } if (!res && audiosink && GST_IS_BIN (audiosink)) { - res = gst_bin_get_all_by_interface (GST_BIN (audiosink), interface_type); + it = gst_bin_iterate_all_by_interface (GST_BIN (audiosink), interface_type); + } + if (it != NULL) { + gpointer data; + + while (gst_iterator_next (it, &data) == GST_ITERATOR_OK) { + res = g_list_prepend (res, data); + } } return res; diff --git a/gst-libs/gst/riff/riff-read.c b/gst-libs/gst/riff/riff-read.c index 21cdbbca4a..f9c895764a 100644 --- a/gst-libs/gst/riff/riff-read.c +++ b/gst-libs/gst/riff/riff-read.c @@ -26,480 +26,190 @@ #include #include -#include "riff-ids.h" #include "riff-read.h" -GST_DEBUG_CATEGORY_STATIC (riffread_debug); -#define GST_CAT_DEFAULT riffread_debug +GST_DEBUG_CATEGORY_EXTERN (riff_debug); +#define GST_CAT_DEFAULT riff_debug -enum -{ - ARG_0, - ARG_METADATA - /* FILL ME */ -}; - -static void gst_riff_read_class_init (GstRiffReadClass * klass); -static void gst_riff_read_init (GstRiffRead * riff); - -static GstElementStateReturn gst_riff_read_change_state (GstElement * element); - -static GstElementClass *parent_class = NULL; - -GType -gst_riff_read_get_type (void) -{ - static GType gst_riff_read_type = 0; - - if (!gst_riff_read_type) { - static const GTypeInfo gst_riff_read_info = { - sizeof (GstRiffReadClass), - NULL, - NULL, - (GClassInitFunc) gst_riff_read_class_init, - NULL, - NULL, - sizeof (GstRiffRead), - 0, - (GInstanceInitFunc) gst_riff_read_init, - }; - - gst_riff_read_type = - g_type_register_static (GST_TYPE_ELEMENT, "GstRiffRead", - &gst_riff_read_info, 0); - } - - return gst_riff_read_type; -} - -static void -gst_riff_read_class_init (GstRiffReadClass * klass) -{ - GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); - - parent_class = g_type_class_ref (GST_TYPE_ELEMENT); - - GST_DEBUG_CATEGORY_INIT (riffread_debug, "riffread", - 0, "RIFF stream helper class"); - - gstelement_class->change_state = gst_riff_read_change_state; -} - -static void -gst_riff_read_init (GstRiffRead * riff) -{ - riff->sinkpad = NULL; - riff->bs = NULL; - riff->level = NULL; -} - -static GstElementStateReturn -gst_riff_read_change_state (GstElement * element) -{ - GstRiffRead *riff = GST_RIFF_READ (element); - - switch (GST_STATE_TRANSITION (element)) { - case GST_STATE_READY_TO_PAUSED: - if (!riff->sinkpad) - return GST_STATE_FAILURE; - riff->bs = gst_bytestream_new (riff->sinkpad); - break; - case GST_STATE_PAUSED_TO_READY: - gst_bytestream_destroy (riff->bs); - while (riff->level) { - GstRiffLevel *level = riff->level->data; - - riff->level = g_list_remove (riff->level, level); - g_free (level); - } - break; - default: - break; - } - - if (GST_ELEMENT_CLASS (parent_class)->change_state) - return GST_ELEMENT_CLASS (parent_class)->change_state (element); - - return GST_STATE_SUCCESS; -} - -/* - * Return: the amount of levels in the hierarchy that the - * current element lies higher than the previous one. - * The opposite isn't done - that's auto-done using list - * element reading. +/** + * gst_riff_read_chunk: + * @element: caller element (used for debugging). + * @pad: pad to pull data from. + * @offset: offset to pull from, incremented by this function. + * @tag: fourcc of the chunk (returned by this function). + * @chunk_data: buffer (returned by this function). + * + * Reads a single chunk of data. + * + * Returns: flow status. */ -static guint -gst_riff_read_element_level_up (GstRiffRead * riff) +GstFlowReturn +gst_riff_read_chunk (GstElement * element, + GstPad * pad, guint64 * _offset, guint32 * tag, GstBuffer ** _chunk_data) { - guint num = 0; - guint64 pos = gst_bytestream_tell (riff->bs); + GstBuffer *buf; + GstFlowReturn res; + guint size; + guint64 offset = *_offset; - while (riff->level != NULL) { - GList *last = g_list_last (riff->level); - GstRiffLevel *level = last->data; - - if (pos >= level->start + level->length) { - riff->level = g_list_remove (riff->level, level); - g_free (level); - num++; - } else - break; - } - - return num; -} - -/* - * Event handler. Basic: - * - EOS: end-of-file, stop processing, forward EOS. - * - Interrupt: stop processing. - * - Discont: shouldn't be handled here but in the seek handler. Error. - * - Flush: ignore, since we check for flush flags manually. Don't forward. - * - Others: warn, ignore. - * Return value indicates whether to continue processing. - */ - -static gboolean -gst_riff_read_use_event (GstRiffRead * riff, GstEvent * event) -{ - if (!event) { - GST_ELEMENT_ERROR (riff, RESOURCE, READ, (NULL), (NULL)); - return FALSE; - } - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_EOS: - gst_pad_event_default (riff->sinkpad, event); - return FALSE; - - case GST_EVENT_INTERRUPT: - gst_event_unref (event); - return FALSE; - - case GST_EVENT_DISCONTINUOUS: - GST_WARNING_OBJECT (riff, "Unexpected discont - might lose sync"); - gst_event_unref (event); - return TRUE; - - case GST_EVENT_FLUSH: - gst_event_unref (event); - return TRUE; - - default: - GST_WARNING ("don't know how to handle event %d", GST_EVENT_TYPE (event)); - gst_pad_event_default (riff->sinkpad, event); - return TRUE; - } - - /* happy */ - g_assert_not_reached (); - return FALSE; -} - -static gboolean -gst_riff_read_handle_event (GstRiffRead * riff) -{ - GstEvent *event = NULL; - guint32 remaining; - - gst_bytestream_get_status (riff->bs, &remaining, &event); - - return gst_riff_read_use_event (riff, event); -} - -/* - * Read the next tag plus length (may be NULL). Return - * TRUE on success or FALSE on failure. - */ - -gboolean -gst_riff_peek_head (GstRiffRead * riff, - guint32 * tag, guint32 * length, guint * level_up) -{ - GList *last; - guint8 *data; - - /* if we're at the end of a chunk, but unaligned, then re-align. - * Those are essentially broken files, but unfortunately they - * exist. */ - if ((last = g_list_last (riff->level)) != NULL) { - GstRiffLevel *level = last->data; - guint64 pos = gst_bytestream_tell (riff->bs); - - if (level->start + level->length - pos < 8) { - if (!gst_bytestream_flush (riff->bs, level->start + level->length - pos)) { - GST_ELEMENT_ERROR (riff, RESOURCE, READ, (NULL), (NULL)); - return FALSE; - } - } - } - - /* read */ - while (gst_bytestream_peek_bytes (riff->bs, &data, 8) != 8) { - if (!gst_riff_read_handle_event (riff)) - return FALSE; - } - - /* parse tag + length (if wanted) */ - *tag = GST_READ_UINT32_LE (data); - if (length) - *length = GST_READ_UINT32_LE (((guint32 *) data) + 1); - - /* level */ - if (level_up) - *level_up = gst_riff_read_element_level_up (riff); - - return TRUE; -} - -/* - * Read: the actual data (plus alignment and flush). - * Return: the data, as a GstBuffer. - */ - -GstBuffer * -gst_riff_peek_element_data (GstRiffRead * riff, guint length, guint * got_bytes) -{ - GstBuffer *buf = NULL; - guint32 got; - - while ((got = gst_bytestream_peek (riff->bs, &buf, length)) != length) { + if ((res = gst_pad_pull_range (pad, offset, 8, &buf)) != GST_FLOW_OK) + return res; + else if (!buf || GST_BUFFER_SIZE (buf) < 8) { if (buf) gst_buffer_unref (buf); - if (!gst_riff_read_handle_event (riff)) - return NULL; + return GST_FLOW_ERROR; } - if (got_bytes) - *got_bytes = got; + *tag = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf)); + size = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf) + 4); + gst_buffer_unref (buf); - return buf; + if ((res = gst_pad_pull_range (pad, offset + 8, size, &buf)) != GST_FLOW_OK) + return res; + else if (!buf || GST_BUFFER_SIZE (buf) < size) { + if (buf) + gst_buffer_unref (buf); + return GST_FLOW_ERROR; + } + + *_chunk_data = buf; + *_offset += 8 + ((size + 1) & ~1); + + return GST_FLOW_OK; } -GstBuffer * -gst_riff_read_element_data (GstRiffRead * riff, guint length, guint * got_bytes) -{ - GstBuffer *buf; - - if (!(buf = gst_riff_peek_element_data (riff, length, got_bytes))) - return NULL; - - /* we need 16-bit alignment */ - if (length & 1) - length++; - - if (!gst_bytestream_flush (riff->bs, length)) { - gst_buffer_unref (buf); - return NULL; - } - - return buf; -} - -/* - * Seek. - */ - -GstEvent * -gst_riff_read_seek (GstRiffRead * riff, guint64 offset) -{ - guint64 length = gst_bytestream_length (riff->bs); - guint32 remaining; - GstEvent *event = NULL; - guchar *data; - - /* hack for AVI files with broken idx1 size chunk markers */ - if (offset > length) - offset = length; - - /* first, flush remaining buffers */ - gst_bytestream_get_status (riff->bs, &remaining, &event); - if (event) { - GST_WARNING ("Unexpected event before seek"); - if (!gst_riff_read_use_event (riff, event)) - return NULL; - event = NULL; - } - - if (remaining) - gst_bytestream_flush_fast (riff->bs, remaining); - - /* now seek */ - if (!gst_bytestream_seek (riff->bs, offset, GST_SEEK_METHOD_SET)) { - GST_ELEMENT_ERROR (riff, RESOURCE, SEEK, (NULL), (NULL)); - return NULL; - } - - /* and now, peek a new byte. This will fail because there's a - * pending event. Then, take the event and return it. */ - while (!event) { - if (gst_bytestream_peek_bytes (riff->bs, &data, 1)) { - GST_WARNING ("Unexpected data after seek - this means seek failed"); - break; - } - - /* get the discont event and return */ - gst_bytestream_get_status (riff->bs, &remaining, &event); - if (!event) { - GST_WARNING ("No discontinuity event after seek - seek failed"); - break; - } else if (GST_EVENT_TYPE (event) != GST_EVENT_DISCONTINUOUS) { - if (!gst_riff_read_use_event (riff, event)) - return NULL; - event = NULL; - } - } - - return event; -} - -/* - * Gives the tag of the next RIFF element. - */ - -guint32 -gst_riff_peek_tag (GstRiffRead * riff, guint * level_up) -{ - guint32 tag; - - if (!gst_riff_peek_head (riff, &tag, NULL, level_up)) - return 0; - - return tag; -} - -/* - * Gives the tag of the next LIST/RIFF element. - */ - -guint32 -gst_riff_peek_list (GstRiffRead * riff) -{ - guint32 lst; - guint8 *data; - - if (!gst_riff_peek_head (riff, &lst, NULL, NULL)) - return FALSE; - if (lst != GST_RIFF_TAG_LIST) { - g_warning ("Not a LIST object"); - return 0; - } - - if (gst_bytestream_peek_bytes (riff->bs, &data, 12) != 12) { - GST_ELEMENT_ERROR (riff, RESOURCE, READ, (NULL), (NULL)); - return 0; - } - - return GST_READ_UINT32_LE (((guint32 *) data) + 2); -} - -/* - * Don't read data. +/** + * gst_riff_parse_chunk: + * @element: caller element (used for debugging). + * @buf: input buffer. + * @offset: offset in the buffer in the caller. Is incremented + * by the read size by this function. + * @fourcc: fourcc (returned by this function0 of the chunk. + * @chunk_data: buffer (returned by the function) containing the + * chunk data. + * + * Reads a single chunk. + * + * Returns: the fourcc tag of this chunk, or 0 on error. */ gboolean -gst_riff_read_skip (GstRiffRead * riff) +gst_riff_parse_chunk (GstElement * element, GstBuffer * buf, + guint * _offset, guint32 * _fourcc, GstBuffer ** chunk_data) { - guint32 tag, length; - GstEvent *event = NULL; - guint32 remaining; + guint size; + guint32 fourcc; + guint8 *data; + guint offset = *_offset; - if (!gst_riff_peek_head (riff, &tag, &length, NULL)) + *chunk_data = NULL; + *_fourcc = 0; + + if (!buf || GST_BUFFER_SIZE (buf) < offset + 8) { + GST_DEBUG_OBJECT (element, + "Failed to parse chunk header (offset %d, %d available, %d needed)", + offset, buf ? GST_BUFFER_DATA (buf) : 0, 8); return FALSE; - - /* 16-bit alignment */ - if (length & 1) - length++; - - /* header itself */ - length += 8; - - /* see if we have that much data available */ - gst_bytestream_get_status (riff->bs, &remaining, &event); - if (event) { - GST_WARNING ("Unexpected event in skip"); - if (!gst_riff_read_use_event (riff, event)) - return FALSE; } - /* yes */ - if (remaining >= length) { - gst_bytestream_flush_fast (riff->bs, length); - return TRUE; + /* read header */ + data = GST_BUFFER_DATA (buf) + offset; + fourcc = GST_READ_UINT32_LE (data); + size = GST_READ_UINT32_LE (data + 4); + + if (GST_BUFFER_SIZE (buf) < size + 8 + offset) { + GST_DEBUG_OBJECT (element, + "Needed chunk data (%d) is more than available (%d), shortcutting", + size, GST_BUFFER_SIZE (buf) - 8 - offset); + size = GST_BUFFER_SIZE (buf) - 8 - offset; } - /* no */ - if (!(event = gst_riff_read_seek (riff, - gst_bytestream_tell (riff->bs) + length))) - return FALSE; - - gst_event_unref (event); + if (size) + *chunk_data = gst_buffer_create_sub (buf, offset + 8, size); + else + *chunk_data = NULL; + *_fourcc = fourcc; + *_offset += 8 + ((size + 1) & ~1); return TRUE; } -/* - * Read any type of data. +/** + * gst_riff_parse_file_header: + * @element: caller element (used for debugging/error). + * @buf: input buffer from which the file header will be parsed, + * should be at least 12 bytes long. + * @doctype: a fourcc (returned by this function) to indicate the + * type of document (according to the header). + * + * Reads the first few bytes from the provided buffer, checks + * if this stream is a RIFF stream, and determines document type. + * The input data is discarded after use. + * + * Returns: FALSE if this is not a RIFF stream (in which case the + * caller should error out; we already throw an error), or TRUE + * if it is. */ gboolean -gst_riff_read_data (GstRiffRead * riff, guint32 * tag, GstBuffer ** buf) +gst_riff_parse_file_header (GstElement * element, + GstBuffer * buf, guint32 * doctype) { - guint32 length; + guint8 *data = GST_BUFFER_DATA (buf); + guint32 tag; - if (!gst_riff_peek_head (riff, tag, &length, NULL)) + if (!buf || GST_BUFFER_SIZE (buf) < 12) { + GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL), + ("Not enough data to parse RIFF header (%d available, %d needed)", + buf ? GST_BUFFER_SIZE (buf) : 0, 12)); + if (buf) + gst_buffer_unref (buf); return FALSE; - gst_bytestream_flush_fast (riff->bs, 8); + } - return ((*buf = gst_riff_read_element_data (riff, length, NULL)) != NULL); -} - -/* - * Read a string. - */ - -gboolean -gst_riff_read_ascii (GstRiffRead * riff, guint32 * tag, gchar ** str) -{ - GstBuffer *buf; - - if (!gst_riff_read_data (riff, tag, &buf)) + tag = GST_READ_UINT32_LE (data); + if (tag != GST_RIFF_TAG_RIFF) { + GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL), + ("Stream is no RIFF stream: " GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (tag))); + gst_buffer_unref (buf); return FALSE; + } - *str = g_malloc (GST_BUFFER_SIZE (buf) + 1); - memcpy (*str, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf)); - (*str)[GST_BUFFER_SIZE (buf)] = '\0'; + *doctype = GST_READ_UINT32_LE (data + 8); gst_buffer_unref (buf); return TRUE; } -/* - * Read media structs. +/** + * gst_riff_parse_strh: + * @element: caller element (used for debugging/error). + * @buf: input data to be used for parsing, stripped from header. + * @strh: a pointer (returned by this function) to a filled-in + * strh structure. Caller should free it. + * + * Parses a strh structure from input data. The input data is + * discarded after use. + * + * Returns: TRUE if parsing succeeded, otherwise FALSE. The stream + * should be skipped on error, but it is not fatal. */ gboolean -gst_riff_read_strh (GstRiffRead * riff, gst_riff_strh ** header) +gst_riff_parse_strh (GstElement * element, + GstBuffer * buf, gst_riff_strh ** _strh) { - guint32 tag; - GstBuffer *buf; gst_riff_strh *strh; - if (!gst_riff_read_data (riff, &tag, &buf)) - return FALSE; - - if (tag != GST_RIFF_TAG_strh) { - g_warning ("Not a strh chunk"); - gst_buffer_unref (buf); - return FALSE; - } - if (GST_BUFFER_SIZE (buf) < sizeof (gst_riff_strh)) { - GST_WARNING ("Too small strh (%d available, %d needed)", - GST_BUFFER_SIZE (buf), (int) sizeof (gst_riff_strh)); - gst_buffer_unref (buf); + if (!buf || GST_BUFFER_SIZE (buf) < sizeof (gst_riff_strh)) { + GST_ERROR_OBJECT (element, + "Too small strh (%d available, %d needed)", + buf ? GST_BUFFER_SIZE (buf) : 0, (int) sizeof (gst_riff_strh)); + if (buf) + gst_buffer_unref (buf); return FALSE; } @@ -528,46 +238,57 @@ gst_riff_read_strh (GstRiffRead * riff, gst_riff_strh ** header) strh->rate = 1; /* debug */ - GST_INFO ("strh tag found"); - GST_INFO (" type " GST_FOURCC_FORMAT, GST_FOURCC_ARGS (strh->type)); - GST_INFO (" fcc_handler " GST_FOURCC_FORMAT, + GST_INFO_OBJECT (element, "strh tag found:"); + GST_INFO_OBJECT (element, " type " GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (strh->type)); + GST_INFO_OBJECT (element, " fcc_handler " GST_FOURCC_FORMAT, GST_FOURCC_ARGS (strh->fcc_handler)); - GST_INFO (" flags 0x%08x", strh->flags); - GST_INFO (" priority %d", strh->priority); - GST_INFO (" init_frames %d", strh->init_frames); - GST_INFO (" scale %d", strh->scale); - GST_INFO (" rate %d", strh->rate); - GST_INFO (" start %d", strh->start); - GST_INFO (" length %d", strh->length); - GST_INFO (" bufsize %d", strh->bufsize); - GST_INFO (" quality %d", strh->quality); - GST_INFO (" samplesize %d", strh->samplesize); + GST_INFO_OBJECT (element, " flags 0x%08x", strh->flags); + GST_INFO_OBJECT (element, " priority %d", strh->priority); + GST_INFO_OBJECT (element, " init_frames %d", strh->init_frames); + GST_INFO_OBJECT (element, " scale %d", strh->scale); + GST_INFO_OBJECT (element, " rate %d", strh->rate); + GST_INFO_OBJECT (element, " start %d", strh->start); + GST_INFO_OBJECT (element, " length %d", strh->length); + GST_INFO_OBJECT (element, " bufsize %d", strh->bufsize); + GST_INFO_OBJECT (element, " quality %d", strh->quality); + GST_INFO_OBJECT (element, " samplesize %d", strh->samplesize); - *header = strh; + *_strh = strh; return TRUE; } +/** + * gst_riff_parse_strf_vids: + * @element: caller element (used for debugging/error). + * @buf: input data to be used for parsing, stripped from header. + * @strf: a pointer (returned by this function) to a filled-in + * strf/vids structure. Caller should free it. + * @data: a pointer (returned by this function) to a buffer + * containing extradata for this particular stream (e.g. + * palette, codec initialization data). + * + * Parses a video stream´s strf structure plus optionally some + * extradata from input data. The input data is discarded after + * use. + * + * Returns: TRUE if parsing succeeded, otherwise FALSE. The stream + * should be skipped on error, but it is not fatal. + */ + gboolean -gst_riff_read_strf_vids_with_data (GstRiffRead * riff, - gst_riff_strf_vids ** header, GstBuffer ** extradata) +gst_riff_parse_strf_vids (GstElement * element, + GstBuffer * buf, gst_riff_strf_vids ** _strf, GstBuffer ** data) { - guint32 tag; - GstBuffer *buf; gst_riff_strf_vids *strf; - if (!gst_riff_read_data (riff, &tag, &buf)) - return FALSE; - - if (tag != GST_RIFF_TAG_strf) { - g_warning ("Not a strf chunk"); - gst_buffer_unref (buf); - return FALSE; - } - if (GST_BUFFER_SIZE (buf) < sizeof (gst_riff_strf_vids)) { - GST_WARNING ("Too small strf_vids (%d available, %d needed)", - GST_BUFFER_SIZE (buf), (int) sizeof (gst_riff_strf_vids)); - gst_buffer_unref (buf); + if (!buf || GST_BUFFER_SIZE (buf) < sizeof (gst_riff_strf_vids)) { + GST_ERROR_OBJECT (element, + "Too small strf_vids (%d available, %d needed)", + buf ? GST_BUFFER_SIZE (buf) : 0, (int) sizeof (gst_riff_strf_vids)); + if (buf) + gst_buffer_unref (buf); return FALSE; } @@ -588,83 +309,71 @@ gst_riff_read_strf_vids_with_data (GstRiffRead * riff, #endif /* size checking */ - *extradata = NULL; + *data = NULL; if (strf->size > GST_BUFFER_SIZE (buf)) { - g_warning ("strf_vids header gave %d bytes data, only %d available", + GST_WARNING_OBJECT (element, + "strf_vids header gave %d bytes data, only %d available", strf->size, GST_BUFFER_SIZE (buf)); strf->size = GST_BUFFER_SIZE (buf); } else if (strf->size < GST_BUFFER_SIZE (buf)) { - gint len; - - len = GST_BUFFER_SIZE (buf) - strf->size; - if (len > 0) { - *extradata = gst_buffer_create_sub (buf, strf->size, len); - } - } else if (strf->size > sizeof (gst_riff_strf_vids)) { - *extradata = gst_buffer_create_sub (buf, - sizeof (gst_riff_strf_vids), strf->size - sizeof (gst_riff_strf_vids)); + *data = gst_buffer_create_sub (buf, strf->size, + GST_BUFFER_SIZE (buf) - strf->size); } /* debug */ - GST_INFO ("strf tag found in context vids:"); - GST_INFO (" size %d", strf->size); - GST_INFO (" width %d", strf->width); - GST_INFO (" height %d", strf->height); - GST_INFO (" planes %d", strf->planes); - GST_INFO (" bit_cnt %d", strf->bit_cnt); - GST_INFO (" compression " GST_FOURCC_FORMAT, + GST_INFO_OBJECT (element, "strf tag found in context vids:"); + GST_INFO_OBJECT (element, " size %d", strf->size); + GST_INFO_OBJECT (element, " width %d", strf->width); + GST_INFO_OBJECT (element, " height %d", strf->height); + GST_INFO_OBJECT (element, " planes %d", strf->planes); + GST_INFO_OBJECT (element, " bit_cnt %d", strf->bit_cnt); + GST_INFO_OBJECT (element, " compression " GST_FOURCC_FORMAT, GST_FOURCC_ARGS (strf->compression)); - GST_INFO (" image_size %d", strf->image_size); - GST_INFO (" xpels_meter %d", strf->xpels_meter); - GST_INFO (" ypels_meter %d", strf->ypels_meter); - GST_INFO (" num_colors %d", strf->num_colors); - GST_INFO (" imp_colors %d", strf->imp_colors); - if (*extradata) - GST_INFO (" %d bytes extra_data", GST_BUFFER_SIZE (*extradata)); + GST_INFO_OBJECT (element, " image_size %d", strf->image_size); + GST_INFO_OBJECT (element, " xpels_meter %d", strf->xpels_meter); + GST_INFO_OBJECT (element, " ypels_meter %d", strf->ypels_meter); + GST_INFO_OBJECT (element, " num_colors %d", strf->num_colors); + GST_INFO_OBJECT (element, " imp_colors %d", strf->imp_colors); + if (*data) + GST_INFO_OBJECT (element, " %d bytes extradata", GST_BUFFER_SIZE (*data)); gst_buffer_unref (buf); - *header = strf; + *_strf = strf; return TRUE; } -/* - * Obsolete, use gst_riff_read_strf_vids_with_data (). +/** + * gst_riff_parse_strf_auds: + * @element: caller element (used for debugging/error). + * @buf: input data to be used for parsing, stripped from header. + * @strf: a pointer (returned by this function) to a filled-in + * strf/auds structure. Caller should free it. + * @data: a pointer (returned by this function) to a buffer + * containing extradata for this particular stream (e.g. + * codec initialization data). + * + * Parses an audio stream´s strf structure plus optionally some + * extradata from input data. The input data is discarded after + * use. + * + * Returns: TRUE if parsing succeeded, otherwise FALSE. The stream + * should be skipped on error, but it is not fatal. */ -gboolean -gst_riff_read_strf_vids (GstRiffRead * riff, gst_riff_strf_vids ** header) -{ - GstBuffer *data = NULL; - gboolean ret; - - ret = gst_riff_read_strf_vids_with_data (riff, header, &data); - if (data) - gst_buffer_unref (data); - - return ret; -} gboolean -gst_riff_read_strf_auds_with_data (GstRiffRead * riff, - gst_riff_strf_auds ** header, GstBuffer ** extradata) +gst_riff_parse_strf_auds (GstElement * element, + GstBuffer * buf, gst_riff_strf_auds ** _strf, GstBuffer ** data) { - guint32 tag; - GstBuffer *buf; gst_riff_strf_auds *strf; - if (!gst_riff_read_data (riff, &tag, &buf)) - return FALSE; - - if (tag != GST_RIFF_TAG_strf && tag != GST_RIFF_TAG_fmt) { - g_warning ("Not a strf chunk"); - gst_buffer_unref (buf); - return FALSE; - } - if (GST_BUFFER_SIZE (buf) < sizeof (gst_riff_strf_auds)) { - GST_WARNING ("Too small strf_auds (%d available, %d needed)", - GST_BUFFER_SIZE (buf), (int) sizeof (gst_riff_strf_auds)); - gst_buffer_unref (buf); + if (!buf || GST_BUFFER_SIZE (buf) < sizeof (gst_riff_strf_auds)) { + GST_ERROR_OBJECT (element, + "Too small strf_auds (%d available, %d needed)", + buf ? GST_BUFFER_SIZE (buf) : 0, (int) sizeof (gst_riff_strf_auds)); + if (buf) + gst_buffer_unref (buf); return FALSE; } @@ -680,74 +389,65 @@ gst_riff_read_strf_auds_with_data (GstRiffRead * riff, #endif /* size checking */ - *extradata = NULL; + *data = NULL; if (GST_BUFFER_SIZE (buf) > sizeof (gst_riff_strf_auds) + 2) { gint len; len = GST_READ_UINT16_LE (&GST_BUFFER_DATA (buf)[16]); if (len + 2 + sizeof (gst_riff_strf_auds) > GST_BUFFER_SIZE (buf)) { - GST_WARNING ("Extradata indicated %d bytes, but only %d available", + GST_WARNING_OBJECT (element, + "Extradata indicated %d bytes, but only %d available", len, GST_BUFFER_SIZE (buf) - 2 - sizeof (gst_riff_strf_auds)); len = GST_BUFFER_SIZE (buf) - 2 - sizeof (gst_riff_strf_auds); } - if (len > 0) { - *extradata = gst_buffer_create_sub (buf, - sizeof (gst_riff_strf_auds) + 2, len); - } + *data = gst_buffer_create_sub (buf, sizeof (gst_riff_strf_auds) + 2, len); } /* debug */ - GST_INFO ("strf tag found in context auds:"); - GST_INFO (" format %d", strf->format); - GST_INFO (" channels %d", strf->channels); - GST_INFO (" rate %d", strf->rate); - GST_INFO (" av_bps %d", strf->av_bps); - GST_INFO (" blockalign %d", strf->blockalign); - GST_INFO (" size %d", strf->size); /* wordsize, not extrasize! */ - if (*extradata) - GST_INFO (" %d bytes extra_data", GST_BUFFER_SIZE (*extradata)); + GST_INFO_OBJECT (element, "strf tag found in context auds:"); + GST_INFO_OBJECT (element, " format %d", strf->format); + GST_INFO_OBJECT (element, " channels %d", strf->channels); + GST_INFO_OBJECT (element, " rate %d", strf->rate); + GST_INFO_OBJECT (element, " av_bps %d", strf->av_bps); + GST_INFO_OBJECT (element, " blockalign %d", strf->blockalign); + GST_INFO_OBJECT (element, " size %d", strf->size); + if (*data) + GST_INFO_OBJECT (element, " %d bytes extradata", GST_BUFFER_SIZE (*data)); gst_buffer_unref (buf); - *header = strf; + *_strf = strf; return TRUE; } -/* - * Obsolete, use gst_riff_read_strf_auds_with_data (). +/** + * gst_riff_parse_strf_iavs: + * @element: caller element (used for debugging/error). + * @buf: input data to be used for parsing, stripped from header. + * @strf: a pointer (returned by this function) to a filled-in + * strf/iavs structure. Caller should free it. + * @data: a pointer (returned by this function) to a buffer + * containing extradata for this particular stream (e.g. + * codec initialization data). + * + * Parses a interleaved (also known as "complex") stream´s strf + * structure plus optionally some extradata from input data. The + * input data is discarded after use. + * + * Returns: TRUE if parsing succeeded, otherwise FALSE. */ -gboolean -gst_riff_read_strf_auds (GstRiffRead * riff, gst_riff_strf_auds ** header) -{ - GstBuffer *data = NULL; - gboolean ret; - - ret = gst_riff_read_strf_auds_with_data (riff, header, &data); - if (data) - gst_buffer_unref (data); - - return ret; -} gboolean -gst_riff_read_strf_iavs (GstRiffRead * riff, gst_riff_strf_iavs ** header) +gst_riff_parse_strf_iavs (GstElement * element, + GstBuffer * buf, gst_riff_strf_iavs ** _strf, GstBuffer ** data) { - guint32 tag; - GstBuffer *buf; gst_riff_strf_iavs *strf; - if (!gst_riff_read_data (riff, &tag, &buf)) - return FALSE; - - if (tag != GST_RIFF_TAG_strf) { - g_warning ("Not a strf chunk"); - gst_buffer_unref (buf); - return FALSE; - } - if (GST_BUFFER_SIZE (buf) < sizeof (gst_riff_strf_iavs)) { - GST_WARNING ("Too small strf_iavs (%d available, %d needed)", - GST_BUFFER_SIZE (buf), (int) sizeof (gst_riff_strf_iavs)); + if (!buf || GST_BUFFER_SIZE (buf) < sizeof (gst_riff_strf_iavs)) { + GST_ERROR_OBJECT (element, + "Too small strf_iavs (%d available, %d needed)", + buf ? GST_BUFFER_SIZE (buf) : 0, (int) sizeof (gst_riff_strf_iavs)); gst_buffer_unref (buf); return FALSE; } @@ -767,85 +467,63 @@ gst_riff_read_strf_iavs (GstRiffRead * riff, gst_riff_strf_iavs ** header) #endif /* debug */ - GST_INFO ("strf tag found in context iavs"); - GST_INFO (" DVAAuxSrc %08x", strf->DVAAuxSrc); - GST_INFO (" DVAAuxCtl %08x", strf->DVAAuxCtl); - GST_INFO (" DVAAuxSrc1 %08x", strf->DVAAuxSrc1); - GST_INFO (" DVAAuxCtl1 %08x", strf->DVAAuxCtl1); - GST_INFO (" DVVAuxSrc %08x", strf->DVVAuxSrc); - GST_INFO (" DVVAuxCtl %08x", strf->DVVAuxCtl); - GST_INFO (" DVReserved1 %08x", strf->DVReserved1); - GST_INFO (" DVReserved2 %08x", strf->DVReserved2); + GST_INFO_OBJECT (element, "strf tag found in context iavs:"); + GST_INFO_OBJECT (element, " DVAAuxSrc %08x", strf->DVAAuxSrc); + GST_INFO_OBJECT (element, " DVAAuxCtl %08x", strf->DVAAuxCtl); + GST_INFO_OBJECT (element, " DVAAuxSrc1 %08x", strf->DVAAuxSrc1); + GST_INFO_OBJECT (element, " DVAAuxCtl1 %08x", strf->DVAAuxCtl1); + GST_INFO_OBJECT (element, " DVVAuxSrc %08x", strf->DVVAuxSrc); + GST_INFO_OBJECT (element, " DVVAuxCtl %08x", strf->DVVAuxCtl); + GST_INFO_OBJECT (element, " DVReserved1 %08x", strf->DVReserved1); + GST_INFO_OBJECT (element, " DVReserved2 %08x", strf->DVReserved2); - *header = strf; + *_strf = strf; + *data = NULL; return TRUE; } -/* - * Read a list. +/** + * gst_riff_parse_info: + * @element: caller element (used for debugging/error). + * @buf: input data to be used for parsing, stripped from header. + * @taglist: a pointer to a taglist (returned by this function) + * containing information about this stream. May be + * NULL if no supported tags were found. + * + * Parses stream metadata from input data. The input data is + * discarded after use. */ -gboolean -gst_riff_read_list (GstRiffRead * riff, guint32 * tag) +void +gst_riff_parse_info (GstElement * element, + GstBuffer * buf, GstTagList ** _taglist) { - guint32 length, lst; - GstRiffLevel *level; guint8 *data; - - if (!gst_riff_peek_head (riff, &lst, &length, NULL)) - return FALSE; - if (lst != GST_RIFF_TAG_LIST) { - g_warning ("Not a LIST object"); - return FALSE; - } - gst_bytestream_flush_fast (riff->bs, 8); - if (gst_bytestream_peek_bytes (riff->bs, &data, 4) != 4) { - GST_ELEMENT_ERROR (riff, RESOURCE, READ, (NULL), (NULL)); - return FALSE; - } - gst_bytestream_flush_fast (riff->bs, 4); - *tag = GST_READ_UINT32_LE (data); - - /* remember level */ - level = g_new (GstRiffLevel, 1); - level->start = gst_bytestream_tell (riff->bs); - level->length = length - 4; - riff->level = g_list_append (riff->level, level); - - return TRUE; -} - -/* - * Utility function for reading metadata in a RIFF file. - */ - -gboolean -gst_riff_read_info (GstRiffRead * riff) -{ + guint size, tsize; guint32 tag; - guint64 end; - GstRiffLevel *level; - GList *last; - gchar *name, *type; + const gchar *type; + gchar *name; GstTagList *taglist; gboolean have_tags = FALSE; - /* What we're doing here is ugly (oh no!); we look - * at our LIST tag size and assure that we do not - * cross boundaries. This is to maintain the level - * counter for the client app. */ - last = g_list_last (riff->level); - level = last->data; - riff->level = g_list_remove (riff->level, level); - end = level->start + level->length; - g_free (level); - + if (!buf) { + *_taglist = NULL; + return; + } + data = GST_BUFFER_DATA (buf); + size = GST_BUFFER_SIZE (buf); taglist = gst_tag_list_new (); - while (gst_bytestream_tell (riff->bs) < end) { - if (!gst_riff_peek_head (riff, &tag, NULL, NULL)) { - return FALSE; + while (size > 8) { + tag = GST_READ_UINT32_LE (data); + tsize = GST_READ_UINT32_LE (data + 4); + size -= 8; + data += 8; + if (tsize > size) { + GST_WARNING_OBJECT (element, + "Tagsize %d is larger than available data %d", tsize, size); + tsize = size; } /* find out the type of metadata */ @@ -921,96 +599,36 @@ gst_riff_read_info (GstRiffRead * riff) break; default: type = NULL; - GST_WARNING ("Unknown INFO (metadata) tag entry " GST_FOURCC_FORMAT, + GST_WARNING_OBJECT (element, + "Unknown INFO (metadata) tag entry " GST_FOURCC_FORMAT, GST_FOURCC_ARGS (tag)); break; } if (type) { - name = NULL; - if (!gst_riff_read_ascii (riff, &tag, &name)) { - return FALSE; - } - - if (name && name[0] != '\0') { - GValue src = { 0 } - , dest = { - 0}; - GType dest_type = gst_tag_get_type (type); + if (data[0] != '\0') { + /* read, NULL-terminate */ + name = g_new (gchar, tsize + 1); + name[tsize] = '\0'; + memcpy (name, data, tsize); + /* add to list */ have_tags = TRUE; - g_value_init (&src, G_TYPE_STRING); - g_value_set_string (&src, name); - g_value_init (&dest, dest_type); - g_value_transform (&src, &dest); - g_value_unset (&src); - gst_tag_list_add_values (taglist, GST_TAG_MERGE_APPEND, - type, &dest, NULL); - g_value_unset (&dest); + gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, type, name, NULL); + g_free (name); } - g_free (name); - } else { - gst_riff_read_skip (riff); } + + data += tsize; + size -= tsize; } if (have_tags) { - GstElement *element = GST_ELEMENT (riff); - GstEvent *event = gst_event_new_tag (taglist); - const GList *padlist; - - /* let the world know about this wonderful thing */ - for (padlist = gst_element_get_pad_list (element); - padlist != NULL; padlist = padlist->next) { - if (GST_PAD_IS_SRC (padlist->data) && GST_PAD_IS_USABLE (padlist->data)) { - gst_event_ref (event); - gst_pad_push (GST_PAD (padlist->data), GST_DATA (event)); - } - } - - gst_element_found_tags (element, taglist); - - gst_event_unref (event); + *_taglist = taglist; } else { + *_taglist = NULL; gst_tag_list_free (taglist); } - return TRUE; -} - -/* - * Read RIFF header and document type. - */ - -gboolean -gst_riff_read_header (GstRiffRead * riff, guint32 * doctype) -{ - GstRiffLevel *level; - guint32 tag, length; - guint8 *data; - - /* We ignore size for openDML-2.0 support */ - if (!gst_riff_peek_head (riff, &tag, &length, NULL)) - return FALSE; - if (tag != GST_RIFF_TAG_RIFF) { - GST_ELEMENT_ERROR (riff, STREAM, WRONG_TYPE, (NULL), (NULL)); - return FALSE; - } - gst_bytestream_flush_fast (riff->bs, 8); - - /* doctype */ - if (gst_bytestream_peek_bytes (riff->bs, &data, 4) != 4) { - GST_ELEMENT_ERROR (riff, RESOURCE, READ, (NULL), (NULL)); - return FALSE; - } - gst_bytestream_flush_fast (riff->bs, 4); - *doctype = GST_READ_UINT32_LE (data); - - /* remember level */ - level = g_new (GstRiffLevel, 1); - level->start = gst_bytestream_tell (riff->bs); - level->length = length - 4; - riff->level = g_list_append (riff->level, level); - - return TRUE; + return; } diff --git a/gst-libs/gst/riff/riff-read.h b/gst-libs/gst/riff/riff-read.h index a0c1260601..aadd0a629e 100644 --- a/gst-libs/gst/riff/riff-read.h +++ b/gst-libs/gst/riff/riff-read.h @@ -24,90 +24,58 @@ #include #include -#include + +#include "riff-ids.h" G_BEGIN_DECLS -#define GST_TYPE_RIFF_READ \ - (gst_riff_read_get_type ()) -#define GST_RIFF_READ(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RIFF_READ, GstRiffRead)) -#define GST_RIFF_READ_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_RIFF_READ, GstRiffReadClass)) -#define GST_IS_RIFF_READ(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RIFF_READ)) -#define GST_IS_RIFF_READ_CLASS(obj) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RIFF_READ)) -#define GST_RIFF_READ_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RIFF_READ, GstRiffReadClass)) - -typedef struct _GstRiffLevel { - guint64 start, - length; -} GstRiffLevel; - -typedef struct _GstRiffRead { - GstElement parent; - - GstPad *sinkpad; - GstByteStream *bs; - - GList *level; -} GstRiffRead; - -typedef struct _GstRiffReadClass { - GstElementClass parent; -} GstRiffReadClass; - -GType gst_riff_read_get_type (void); - -guint32 gst_riff_peek_tag (GstRiffRead *riff, - guint *level_up); -guint32 gst_riff_peek_list (GstRiffRead *riff); -gboolean gst_riff_peek_head (GstRiffRead *riff, - guint32 *tag, - guint32 *length, - guint *level_up); - -GstEvent *gst_riff_read_seek (GstRiffRead *riff, - guint64 offset); -gboolean gst_riff_read_skip (GstRiffRead *riff); -gboolean gst_riff_read_data (GstRiffRead *riff, - guint32 *tag, - GstBuffer **buf); -gboolean gst_riff_read_ascii (GstRiffRead *riff, - guint32 *tag, - gchar **str); -gboolean gst_riff_read_list (GstRiffRead *riff, - guint32 *tag); -gboolean gst_riff_read_header (GstRiffRead *read, - guint32 *doctype); -GstBuffer *gst_riff_read_element_data (GstRiffRead *riff, - guint length, - guint *got_bytes); -GstBuffer *gst_riff_peek_element_data (GstRiffRead *riff, - guint length, - guint *got_bytes); /* - * Utility functions (including byteswapping). + * Operate using pull_range(). */ -gboolean gst_riff_read_strh (GstRiffRead *riff, - gst_riff_strh **header); -gboolean gst_riff_read_strf_vids (GstRiffRead *riff, - gst_riff_strf_vids **header); -gboolean gst_riff_read_strf_vids_with_data - (GstRiffRead *riff, - gst_riff_strf_vids **header, - GstBuffer **extradata); -gboolean gst_riff_read_strf_auds (GstRiffRead *riff, - gst_riff_strf_auds **header); -gboolean gst_riff_read_strf_auds_with_data - (GstRiffRead *riff, - gst_riff_strf_auds **header, - GstBuffer **extradata); -gboolean gst_riff_read_strf_iavs (GstRiffRead *riff, - gst_riff_strf_iavs **header); -gboolean gst_riff_read_info (GstRiffRead *riff); + +GstFlowReturn gst_riff_read_chunk (GstElement * element, + GstPad * pad, + guint64 * offset, + guint32 * fourcc, + GstBuffer ** chunk_data); + +/* + * These functions operate on provided data (the caller is + * supposed to strip the chunk headers). The buffer is + * provided by the caller, the strf/strh/data are filled in + * by the function. + */ + +gboolean gst_riff_parse_chunk (GstElement * element, + GstBuffer * buf, + guint * offset, + guint32 * fourcc, + GstBuffer ** chunk_data); + +gboolean gst_riff_parse_file_header (GstElement * element, + GstBuffer * buf, + guint32 * doctype); + +gboolean gst_riff_parse_strh (GstElement * element, + GstBuffer * buf, + gst_riff_strh ** strh); + +gboolean gst_riff_parse_strf_vids (GstElement * element, + GstBuffer * buf, + gst_riff_strf_vids ** strf, + GstBuffer ** data); +gboolean gst_riff_parse_strf_auds (GstElement * element, + GstBuffer * buf, + gst_riff_strf_auds ** strf, + GstBuffer ** data); +gboolean gst_riff_parse_strf_iavs (GstElement * element, + GstBuffer * buf, + gst_riff_strf_iavs ** strf, + GstBuffer ** data); + +void gst_riff_parse_info (GstElement * element, + GstBuffer * buf, + GstTagList ** taglist); G_END_DECLS diff --git a/gst-libs/gst/riff/riff.c b/gst-libs/gst/riff/riff.c index ab8d47b57f..433cac7661 100644 --- a/gst-libs/gst/riff/riff.c +++ b/gst-libs/gst/riff/riff.c @@ -25,10 +25,14 @@ #include +GST_DEBUG_CATEGORY (riff_debug); + static gboolean plugin_init (GstPlugin * plugin) { - return gst_library_load ("gstbytestream"); + GST_DEBUG_CATEGORY_INIT (riff_debug, "riff", 0, "RIFF I/O"); + + return TRUE; } GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, diff --git a/gst-libs/gst/video/Makefile.am b/gst-libs/gst/video/Makefile.am index 7a163773b5..b79156ba44 100644 --- a/gst-libs/gst/video/Makefile.am +++ b/gst-libs/gst/video/Makefile.am @@ -9,4 +9,4 @@ libgstvideoinclude_HEADERS = video.h videosink.h libgstvideo_la_LIBADD = libgstvideo_la_CFLAGS = $(GST_CFLAGS) -libgstvideo_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstvideo_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) ../../../../gstreamer-NEWHEAD/gst/base/libgstbase.la diff --git a/gst-libs/gst/video/gstvideosink.c b/gst-libs/gst/video/gstvideosink.c index 1085ace950..2d06b3157c 100644 --- a/gst-libs/gst/video/gstvideosink.c +++ b/gst-libs/gst/video/gstvideosink.c @@ -27,17 +27,6 @@ static GstElementClass *parent_class = NULL; -/* Private methods */ - -static void -gst_videosink_set_clock (GstElement * element, GstClock * clock) -{ - GstVideoSink *videosink; - - videosink = GST_VIDEOSINK (element); - - videosink->clock = clock; -} /* Initing stuff */ @@ -46,7 +35,6 @@ gst_videosink_init (GstVideoSink * videosink) { videosink->width = 0; videosink->height = 0; - videosink->clock = NULL; } static void @@ -59,8 +47,6 @@ gst_videosink_class_init (GstVideoSinkClass * klass) gstelement_class = (GstElementClass *) klass; parent_class = g_type_class_ref (GST_TYPE_ELEMENT); - - gstelement_class->set_clock = gst_videosink_set_clock; } /* Public methods */ @@ -83,7 +69,7 @@ gst_videosink_get_type (void) (GInstanceInitFunc) gst_videosink_init, }; - videosink_type = g_type_register_static (GST_TYPE_ELEMENT, + videosink_type = g_type_register_static (GST_TYPE_BASESINK, "GstVideoSink", &videosink_info, 0); } diff --git a/gst-libs/gst/video/gstvideosink.h b/gst-libs/gst/video/gstvideosink.h index bdf52e1534..804d2e5242 100644 --- a/gst-libs/gst/video/gstvideosink.h +++ b/gst-libs/gst/video/gstvideosink.h @@ -23,6 +23,7 @@ #define __GST_VIDEOSINK_H__ #include +#include G_BEGIN_DECLS @@ -38,28 +39,24 @@ G_BEGIN_DECLS #define GST_VIDEOSINK_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_VIDEOSINK, GstVideoSinkClass)) -#define GST_VIDEOSINK_PAD(obj) (GST_VIDEOSINK (obj)->sinkpad) +#define GST_VIDEOSINK_PAD GST_BASESINK_PAD +#define GST_VIDEOSINK_CLOCK GST_BASESINK_CLOCK #define GST_VIDEOSINK_WIDTH(obj) (GST_VIDEOSINK (obj)->width) #define GST_VIDEOSINK_HEIGHT(obj) (GST_VIDEOSINK (obj)->height) -#define GST_VIDEOSINK_CLOCK(obj) (GST_VIDEOSINK (obj)->clock) typedef struct _GstVideoSink GstVideoSink; typedef struct _GstVideoSinkClass GstVideoSinkClass; struct _GstVideoSink { - GstElement element; - - GstPad *sinkpad; + GstBaseSink element; gint width, height; - GstClock *clock; - gpointer _gst_reserved[GST_PADDING]; }; struct _GstVideoSinkClass { - GstElementClass parent_class; + GstBaseSinkClass parent_class; gpointer _gst_reserved[GST_PADDING]; }; diff --git a/gst-libs/gst/video/videosink.h b/gst-libs/gst/video/videosink.h index bdf52e1534..804d2e5242 100644 --- a/gst-libs/gst/video/videosink.h +++ b/gst-libs/gst/video/videosink.h @@ -23,6 +23,7 @@ #define __GST_VIDEOSINK_H__ #include +#include G_BEGIN_DECLS @@ -38,28 +39,24 @@ G_BEGIN_DECLS #define GST_VIDEOSINK_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_VIDEOSINK, GstVideoSinkClass)) -#define GST_VIDEOSINK_PAD(obj) (GST_VIDEOSINK (obj)->sinkpad) +#define GST_VIDEOSINK_PAD GST_BASESINK_PAD +#define GST_VIDEOSINK_CLOCK GST_BASESINK_CLOCK #define GST_VIDEOSINK_WIDTH(obj) (GST_VIDEOSINK (obj)->width) #define GST_VIDEOSINK_HEIGHT(obj) (GST_VIDEOSINK (obj)->height) -#define GST_VIDEOSINK_CLOCK(obj) (GST_VIDEOSINK (obj)->clock) typedef struct _GstVideoSink GstVideoSink; typedef struct _GstVideoSinkClass GstVideoSinkClass; struct _GstVideoSink { - GstElement element; - - GstPad *sinkpad; + GstBaseSink element; gint width, height; - GstClock *clock; - gpointer _gst_reserved[GST_PADDING]; }; struct _GstVideoSinkClass { - GstElementClass parent_class; + GstBaseSinkClass parent_class; gpointer _gst_reserved[GST_PADDING]; }; diff --git a/gst/audioconvert/bufferframesconvert.c b/gst/audioconvert/bufferframesconvert.c index 122b553634..e760ee1665 100644 --- a/gst/audioconvert/bufferframesconvert.c +++ b/gst/audioconvert/bufferframesconvert.c @@ -91,12 +91,11 @@ static GstElementStateReturn buffer_frames_convert_change_state (GstElement * element); static GstCaps *buffer_frames_convert_getcaps (GstPad * pad); -static GstPadLinkReturn buffer_frames_convert_link (GstPad * pad, - const GstCaps * caps); -static GstCaps *buffer_frames_convert_fixate (GstPad * pad, - const GstCaps * caps); +static gboolean buffer_frames_convert_setcaps (GstPad * pad, GstCaps * caps); +static GstCaps *buffer_frames_convert_fixate (GstPad * pad, GstCaps * caps); -static void buffer_frames_convert_chain (GstPad * sinkpad, GstData * _data); +static GstFlowReturn buffer_frames_convert_chain (GstPad * sinkpad, + GstBuffer * buffer); static GstElementClass *parent_class = NULL; @@ -153,17 +152,17 @@ buffer_frames_convert_init (BufferFramesConvert * this) this->sinkpad = gst_pad_new_from_template (gst_static_pad_template_get (&sink_static_template), "sink"); gst_element_add_pad (GST_ELEMENT (this), this->sinkpad); - gst_pad_set_link_function (this->sinkpad, buffer_frames_convert_link); + gst_pad_set_setcaps_function (this->sinkpad, buffer_frames_convert_setcaps); gst_pad_set_getcaps_function (this->sinkpad, buffer_frames_convert_getcaps); gst_pad_set_chain_function (this->sinkpad, buffer_frames_convert_chain); - gst_pad_set_fixate_function (this->sinkpad, buffer_frames_convert_fixate); + gst_pad_set_fixatecaps_function (this->sinkpad, buffer_frames_convert_fixate); this->srcpad = gst_pad_new_from_template (gst_static_pad_template_get (&src_static_template), "src"); gst_element_add_pad (GST_ELEMENT (this), this->srcpad); - gst_pad_set_link_function (this->srcpad, buffer_frames_convert_link); + gst_pad_set_setcaps_function (this->srcpad, buffer_frames_convert_setcaps); gst_pad_set_getcaps_function (this->srcpad, buffer_frames_convert_getcaps); - gst_pad_set_fixate_function (this->sinkpad, buffer_frames_convert_fixate); + gst_pad_set_fixatecaps_function (this->sinkpad, buffer_frames_convert_fixate); this->in_buffer_samples = -1; this->out_buffer_samples = -1; @@ -221,7 +220,7 @@ buffer_frames_convert_getcaps (GstPad * pad) } static GstCaps * -buffer_frames_convert_fixate (GstPad * pad, const GstCaps * caps) +buffer_frames_convert_fixate (GstPad * pad, GstCaps * caps) { GstCaps *newcaps; GstStructure *structure; @@ -235,14 +234,15 @@ buffer_frames_convert_fixate (GstPad * pad, const GstCaps * caps) return newcaps; } - gst_caps_free (newcaps); + gst_caps_unref (newcaps); return NULL; } -static GstPadLinkReturn -buffer_frames_convert_link (GstPad * pad, const GstCaps * caps) +static gboolean +buffer_frames_convert_setcaps (GstPad * pad, GstCaps * caps) { +#if 0 BufferFramesConvert *this; GstCaps *othercaps; GstPad *otherpad; @@ -255,7 +255,7 @@ buffer_frames_convert_link (GstPad * pad, const GstCaps * caps) otherpad = pad == this->srcpad ? this->sinkpad : this->srcpad; /* first try to act as a passthrough */ - ret = gst_pad_try_set_caps (otherpad, caps); + ret = gst_pad_set_caps (otherpad, caps); if (GST_PAD_LINK_SUCCESSFUL (ret)) { this->passthrough = TRUE; return ret; @@ -266,10 +266,8 @@ buffer_frames_convert_link (GstPad * pad, const GstCaps * caps) gst_caps_set_simple (othercaps, "buffer-frames", GST_TYPE_INT_RANGE, 0, G_MAXINT, NULL); ret = gst_pad_try_set_caps_nonfixed (otherpad, othercaps); - gst_caps_free (othercaps); if (GST_PAD_LINK_FAILED (ret)) return ret; - othercaps = gst_caps_copy (gst_pad_get_negotiated_caps (otherpad)); /* it's ok, let's record our data */ sinkstructure = @@ -284,16 +282,16 @@ buffer_frames_convert_link (GstPad * pad, const GstCaps * caps) gst_structure_get_int (sinkstructure, "channels", &numchannels); this->in_buffer_samples *= numchannels; this->out_buffer_samples *= numchannels; - gst_caps_free (othercaps); if (this->out_buffer_samples == 0) this->passthrough = TRUE; +#endif - return GST_PAD_LINK_OK; + return TRUE; } -static void -buffer_frames_convert_chain (GstPad * pad, GstData * _data) +static GstFlowReturn +buffer_frames_convert_chain (GstPad * pad, GstBuffer * buf) { BufferFramesConvert *this; GstBuffer *buf_in, *buf_out; @@ -305,11 +303,10 @@ buffer_frames_convert_chain (GstPad * pad, GstData * _data) this = (BufferFramesConvert *) GST_OBJECT_PARENT (pad); if (this->passthrough) { - gst_pad_push (this->srcpad, _data); - return; + return gst_pad_push (this->srcpad, buf); } - buf_in = (GstBuffer *) _data; + buf_in = buf; data_in = (gfloat *) GST_BUFFER_DATA (buf_in); samples_in = samples_in_remaining = GST_BUFFER_SIZE (buf_in) / sizeof (gfloat); @@ -331,12 +328,12 @@ buffer_frames_convert_chain (GstPad * pad, GstData * _data) if (!samples_out_remaining) { this->buf_out = NULL; this->samples_out_remaining = 0; - gst_pad_push (this->srcpad, (GstData *) buf_out); + gst_pad_push (this->srcpad, buf_out); } else { /* we used up the incoming samples, but didn't fill our buffer */ this->samples_out_remaining = samples_out_remaining; gst_buffer_unref (buf_in); - return; + return GST_FLOW_OK; } } @@ -347,7 +344,7 @@ buffer_frames_convert_chain (GstPad * pad, GstData * _data) out_buffer_samples * sizeof (gfloat)); data_in += out_buffer_samples; samples_in_remaining -= out_buffer_samples; - gst_pad_push (this->srcpad, (GstData *) buf_out); + gst_pad_push (this->srcpad, buf_out); } /* if there's an event coming next, just push what we have */ @@ -357,13 +354,13 @@ buffer_frames_convert_chain (GstPad * pad, GstData * _data) gst_buffer_create_sub (buf_in, (samples_in - samples_in_remaining) * sizeof (gfloat), samples_in_remaining * sizeof (gfloat)); - gst_pad_push (this->srcpad, (GstData *) buf_out); + gst_pad_push (this->srcpad, buf_out); } else { /* otherwise make a leftover buffer if it's necessary */ if (samples_in_remaining) { buf_out = gst_pad_alloc_buffer (this->srcpad, 0, - out_buffer_samples * sizeof (gfloat)); + out_buffer_samples * sizeof (gfloat), GST_PAD_CAPS (this->srcpad)); data_out = (gfloat *) GST_BUFFER_DATA (buf_out); this->buf_out = buf_out; this->samples_out_remaining = out_buffer_samples - samples_in_remaining; @@ -373,4 +370,6 @@ buffer_frames_convert_chain (GstPad * pad, GstData * _data) } gst_buffer_unref (buf_in); + + return GST_FLOW_OK; } diff --git a/gst/audioconvert/channelmixtest.c b/gst/audioconvert/channelmixtest.c index 0bfbf329d3..1f5a429070 100644 --- a/gst/audioconvert/channelmixtest.c +++ b/gst/audioconvert/channelmixtest.c @@ -65,12 +65,12 @@ main (gint argc, gchar ** argv) g_assert (caps); if (!gst_element_link_filtered (src, GST_ELEMENT (c), caps)) g_assert_not_reached (); - gst_caps_free (caps); + gst_caps_unref (caps); caps = gst_caps_from_string (tests[i].srccaps); g_assert (caps); if (!gst_element_link_filtered (GST_ELEMENT (c), sink, caps)) g_assert_not_reached (); - gst_caps_free (caps); + gst_caps_unref (caps); if (!gst_element_set_state (bin, GST_STATE_PLAYING)) g_assert_not_reached (); g_assert (c->srccaps.channels <= 6); diff --git a/gst/audioconvert/gstaudioconvert.c b/gst/audioconvert/gstaudioconvert.c index 4c232d5e67..2df979ff30 100644 --- a/gst/audioconvert/gstaudioconvert.c +++ b/gst/audioconvert/gstaudioconvert.c @@ -60,10 +60,9 @@ static void gst_audio_convert_init (GstAudioConvert * audio_convert); static void gst_audio_convert_dispose (GObject * obj); /* gstreamer functions */ -static void gst_audio_convert_chain (GstPad * pad, GstData * _data); -static GstPadLinkReturn gst_audio_convert_link (GstPad * pad, - const GstCaps * caps); -static GstCaps *gst_audio_convert_fixate (GstPad * pad, const GstCaps * caps); +static GstFlowReturn gst_audio_convert_chain (GstPad * pad, GstBuffer * buffer); +static gboolean gst_audio_convert_setcaps (GstPad * pad, GstCaps * caps); +static GstCaps *gst_audio_convert_fixate (GstPad * pad, GstCaps * caps); static GstCaps *gst_audio_convert_getcaps (GstPad * pad); static GstElementStateReturn gst_audio_convert_change_state (GstElement * element); @@ -187,8 +186,8 @@ gst_audio_convert_init (GstAudioConvert * this) gst_pad_new_from_template (gst_static_pad_template_get (&gst_audio_convert_sink_template), "sink"); gst_pad_set_getcaps_function (this->sink, gst_audio_convert_getcaps); - gst_pad_set_link_function (this->sink, gst_audio_convert_link); - gst_pad_set_fixate_function (this->sink, gst_audio_convert_fixate); + gst_pad_set_setcaps_function (this->sink, gst_audio_convert_setcaps); + gst_pad_set_fixatecaps_function (this->sink, gst_audio_convert_fixate); gst_element_add_pad (GST_ELEMENT (this), this->sink); /* srcpad */ @@ -196,8 +195,8 @@ gst_audio_convert_init (GstAudioConvert * this) gst_pad_new_from_template (gst_static_pad_template_get (&gst_audio_convert_src_template), "src"); gst_pad_set_getcaps_function (this->src, gst_audio_convert_getcaps); - gst_pad_set_link_function (this->src, gst_audio_convert_link); - gst_pad_set_fixate_function (this->src, gst_audio_convert_fixate); + gst_pad_set_setcaps_function (this->src, gst_audio_convert_setcaps); + gst_pad_set_fixatecaps_function (this->src, gst_audio_convert_fixate); gst_element_add_pad (GST_ELEMENT (this), this->src); gst_pad_set_chain_function (this->sink, gst_audio_convert_chain); @@ -228,34 +227,28 @@ gst_audio_convert_dispose (GObject * obj) /*** GSTREAMER FUNCTIONS ******************************************************/ -static void -gst_audio_convert_chain (GstPad * pad, GstData * data) +static GstFlowReturn +gst_audio_convert_chain (GstPad * pad, GstBuffer * buf) { - GstBuffer *buf = GST_BUFFER (data); GstAudioConvert *this; - g_return_if_fail (GST_IS_PAD (pad)); - g_return_if_fail (buf != NULL); - g_return_if_fail (GST_IS_AUDIO_CONVERT (GST_OBJECT_PARENT (pad))); this = GST_AUDIO_CONVERT (GST_OBJECT_PARENT (pad)); /* FIXME */ - if (GST_IS_EVENT (buf)) { - gst_pad_event_default (pad, GST_EVENT (buf)); - return; - } - g_return_if_fail (GST_IS_BUFFER (buf)); - if (!gst_pad_is_negotiated (this->sink)) { +#if 0 + if (!GST_PAD_CAPS (this->sink)) { GST_ELEMENT_ERROR (this, CORE, NEGOTIATION, (NULL), ("Sink pad (connected to %s:%s) not negotiated before chain function", GST_DEBUG_PAD_NAME (gst_pad_get_peer (this->sink)))); - return; + gst_buffer_unref (buf); + return GST_FLOW_NOT_NEGOTIATED; } - if (!gst_pad_is_negotiated (this->src)) { - gst_data_unref (data); - return; + if (!GST_PAD_CAPS (this->src)) { + gst_buffer_unref (buf); + return GST_FLOW_NOT_NEGOTIATED; } +#endif /** * Theory of operation: @@ -271,35 +264,25 @@ gst_audio_convert_chain (GstPad * pad, GstData * data) buf = gst_audio_convert_buffer_from_default_format (this, buf); - gst_pad_push (this->src, GST_DATA (buf)); + return gst_pad_push (this->src, buf); } -/* this function is complicated now, but it will be unnecessary when we convert - * rate. */ static GstCaps * -gst_audio_convert_getcaps (GstPad * pad) +gst_audio_convert_caps_remove_format_info (GstPad * pad, GstCaps * caps) { - GstAudioConvert *this; - GstPad *otherpad; - GstStructure *structure; - GstCaps *othercaps, *caps; - const GstCaps *templcaps; int i, size; + GstAudioConvert *this; - g_return_val_if_fail (GST_IS_PAD (pad), NULL); - g_return_val_if_fail (GST_IS_AUDIO_CONVERT (GST_OBJECT_PARENT (pad)), NULL); this = GST_AUDIO_CONVERT (GST_OBJECT_PARENT (pad)); - otherpad = (pad == this->src) ? this->sink : this->src; + size = gst_caps_get_size (caps); - /* all we want to find out is the rate */ - templcaps = gst_pad_get_pad_template_caps (pad); - othercaps = gst_pad_get_allowed_caps (otherpad); - - size = gst_caps_get_size (othercaps); + caps = gst_caps_make_writable (caps); for (i = size - 1; i >= 0; i--) { - structure = gst_caps_get_structure (othercaps, i); + GstStructure *structure; + + structure = gst_caps_get_structure (caps, i); gst_structure_remove_field (structure, "channels"); gst_structure_remove_field (structure, "channel-positions"); gst_structure_remove_field (structure, "endianness"); @@ -319,10 +302,34 @@ gst_audio_convert_getcaps (GstPad * pad) gst_structure_set_name (structure, "audio/x-raw-int"); gst_structure_remove_field (structure, "buffer-frames"); } - gst_caps_append_structure (othercaps, structure); + gst_caps_append_structure (caps, structure); } + + return caps; +} + +/* this function is complicated now, but it will be unnecessary when we convert + * rate. */ +static GstCaps * +gst_audio_convert_getcaps (GstPad * pad) +{ + GstAudioConvert *this; + GstPad *otherpad; + GstCaps *othercaps, *caps; + const GstCaps *templcaps; + + this = GST_AUDIO_CONVERT (GST_OBJECT_PARENT (pad)); + + otherpad = (pad == this->src) ? this->sink : this->src; + + /* we can do all our peer can */ + othercaps = gst_pad_peer_get_caps (otherpad); + /* without the format info even */ + othercaps = gst_audio_convert_caps_remove_format_info (pad, othercaps); + /* but filtered against our template */ + templcaps = gst_pad_get_pad_template_caps (pad); caps = gst_caps_intersect (othercaps, templcaps); - gst_caps_free (othercaps); + gst_caps_unref (othercaps); /* Get the channel positions in as well. */ gst_audio_set_caps_channel_positions_list (caps, supported_positions, @@ -337,6 +344,8 @@ gst_audio_convert_parse_caps (const GstCaps * gst_caps, { GstStructure *structure = gst_caps_get_structure (gst_caps, 0); + GST_DEBUG ("parse caps %p and %" GST_PTR_FORMAT, gst_caps, gst_caps); + g_return_val_if_fail (gst_caps_is_fixed (gst_caps), FALSE); g_return_val_if_fail (caps != NULL, FALSE); @@ -375,76 +384,72 @@ gst_audio_convert_parse_caps (const GstCaps * gst_caps, return TRUE; } -static GstPadLinkReturn -gst_audio_convert_link (GstPad * pad, const GstCaps * caps) +static gboolean +gst_audio_convert_setcaps (GstPad * pad, GstCaps * caps) { GstAudioConvert *this; GstPad *otherpad; - GstAudioConvertCaps ac_caps = { 0 }, other_ac_caps = { - 0}; - GstCaps *othercaps; - guint i; - GstPadLinkReturn ret; + GstAudioConvertCaps ac_caps = { 0 }; + GstAudioConvertCaps other_ac_caps = { 0 }; + GstCaps **other_prefered, **prefered; - g_return_val_if_fail (GST_IS_PAD (pad), GST_PAD_LINK_REFUSED); - g_return_val_if_fail (GST_IS_AUDIO_CONVERT (GST_OBJECT_PARENT (pad)), - GST_PAD_LINK_REFUSED); + g_return_val_if_fail (GST_IS_PAD (pad), FALSE); + g_return_val_if_fail (GST_IS_AUDIO_CONVERT (GST_OBJECT_PARENT (pad)), FALSE); + g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE); this = GST_AUDIO_CONVERT (GST_OBJECT_PARENT (pad)); - otherpad = (pad == this->src ? this->sink : this->src); /* we'll need a new matrix after every new negotiation */ gst_audio_convert_unset_matrix (this); ac_caps.pos = NULL; if (!gst_audio_convert_parse_caps (caps, &ac_caps)) - return GST_PAD_LINK_REFUSED; + return FALSE; - /* ok, not those - try setting "any" caps */ - othercaps = gst_pad_get_allowed_caps (otherpad); - for (i = 0; i < gst_caps_get_size (othercaps); i++) { - GstStructure *structure = gst_caps_get_structure (othercaps, i); + otherpad = (pad == this->src ? this->sink : this->src); + prefered = (pad == this->src) ? &this->src_prefered : &this->sink_prefered; + other_prefered = + (pad == this->src) ? &this->sink_prefered : &this->src_prefered; - gst_structure_set (structure, "rate", G_TYPE_INT, ac_caps.rate, NULL); - if (strcmp (gst_structure_get_name (structure), "audio/x-raw-float") == 0) { - if (!ac_caps.is_int) { - gst_structure_set (structure, "buffer-frames", G_TYPE_INT, - ac_caps.buffer_frames, NULL); - } else { - gst_structure_set (structure, "buffer-frames", GST_TYPE_INT_RANGE, 0, - G_MAXINT, NULL); + *prefered = caps; + + /* check passthrough */ + if (gst_pad_peer_accept_caps (otherpad, caps)) { + /* great, so that will be our suggestion then */ + *other_prefered = gst_caps_ref (caps); + } else { + /* nope, find something we can convert to and the peer can + * accept. */ + GstCaps *othercaps = gst_pad_peer_get_caps (otherpad); + + if (othercaps) { + /* peel off first one */ + GstCaps *targetcaps = gst_caps_copy_nth (othercaps, 0); + GstStructure *structure = gst_caps_get_structure (targetcaps, 0); + + gst_caps_unref (othercaps); + + /* set the rate on the caps */ + gst_structure_set (structure, "rate", G_TYPE_INT, ac_caps.rate, NULL); + gst_structure_set (structure, "channels", G_TYPE_INT, ac_caps.channels, + NULL); + if (strcmp (gst_structure_get_name (structure), "audio/x-raw-float") == 0) { + if (!ac_caps.is_int) { + gst_structure_set (structure, "buffer-frames", G_TYPE_INT, + ac_caps.buffer_frames, NULL); + } else { + gst_structure_set (structure, "buffer-frames", GST_TYPE_INT_RANGE, 0, + G_MAXINT, NULL); + } } + + /* this will be our suggestion */ + *other_prefered = targetcaps; + if (!gst_audio_convert_parse_caps (targetcaps, &other_ac_caps)) + return FALSE; + GST_RPAD_CAPS (otherpad) = targetcaps; } } - if (this->sink == pad) { - g_free (this->sinkcaps.pos); - this->sinkcaps = ac_caps; - } else { - g_free (this->srccaps.pos); - this->srccaps = ac_caps; - } - GST_LOG_OBJECT (this, "trying to set caps to %" GST_PTR_FORMAT, othercaps); - - ret = gst_pad_try_set_caps_nonfixed (otherpad, othercaps); - gst_caps_free (othercaps); - if (ret < GST_PAD_LINK_OK) - return ret; - - /* woohoo, got it */ - othercaps = (GstCaps *) gst_pad_get_negotiated_caps (otherpad); - if (othercaps) { - other_ac_caps.pos = NULL; - - if (!gst_audio_convert_parse_caps (othercaps, &other_ac_caps)) { - g_critical ("internal negotiation error"); - return GST_PAD_LINK_REFUSED; - } - } else { - other_ac_caps = ac_caps; - other_ac_caps.pos = g_memdup (ac_caps.pos, - ac_caps.channels * sizeof (GstAudioChannelPosition)); - } - if (this->sink == pad) { g_free (this->srccaps.pos); this->srccaps = other_ac_caps; @@ -457,7 +462,7 @@ gst_audio_convert_link (GstPad * pad, const GstCaps * caps) GST_DEBUG_OBJECT (this, "negotiated pad to %" GST_PTR_FORMAT, caps); gst_audio_convert_setup_matrix (this); - return GST_PAD_LINK_OK; + return TRUE; } /* tries to fixate the given field of the given caps to the given int value */ @@ -475,7 +480,7 @@ _fixate_caps_to_int (GstCaps ** caps, const gchar * field, gint value) gst_caps_append (try, gst_caps_new_simple ("audio/x-raw-float", field, GST_TYPE_INT_RANGE, G_MININT, value - 1, NULL)); isect_lower = gst_caps_intersect (*caps, try); - gst_caps_free (try); + gst_caps_unref (try); if (!gst_caps_is_empty (isect_lower)) { try = gst_caps_new_simple ("audio/x-raw-int", field, GST_TYPE_INT_RANGE, @@ -483,18 +488,18 @@ _fixate_caps_to_int (GstCaps ** caps, const gchar * field, gint value) gst_caps_append (try, gst_caps_new_simple ("audio/x-raw-float", field, GST_TYPE_INT_RANGE, value, G_MAXINT, NULL)); isect_higher = gst_caps_intersect (*caps, try); - gst_caps_free (try); + gst_caps_unref (try); /* FIXME: why choose to end up with the higher range, and not the fixed * value ? */ if (!gst_caps_is_empty (isect_higher)) { - gst_caps_free (*caps); + gst_caps_unref (*caps); *caps = isect_higher; ret = TRUE; } else { - gst_caps_free (isect_higher); + gst_caps_unref (isect_higher); } } - gst_caps_free (isect_lower); + gst_caps_unref (isect_lower); /* FIXME: why don't we already return here when ret == TRUE ? */ for (i = 0; i < gst_caps_get_size (*caps); i++) { @@ -508,27 +513,29 @@ _fixate_caps_to_int (GstCaps ** caps, const gchar * field, gint value) } static GstCaps * -gst_audio_convert_fixate (GstPad * pad, const GstCaps * caps) +gst_audio_convert_fixate (GstPad * pad, GstCaps * caps) { const GValue *pos_val; GstAudioConvert *this = GST_AUDIO_CONVERT (gst_object_get_parent (GST_OBJECT (pad))); - GstPad *otherpad = (pad == this->sink ? this->src : this->sink); + //GstPad *otherpad = (pad == this->sink ? this->src : this->sink); GstAudioConvertCaps try, ac_caps = (pad == this->sink ? this->srccaps : this->sinkcaps); GstCaps *copy = gst_caps_copy (caps); - if (!GST_PAD_IS_NEGOTIATING (otherpad)) { - try.channels = 2; - try.width = 16; - try.depth = 16; - try.endianness = G_BYTE_ORDER; - } else { - try.channels = ac_caps.channels; - try.width = ac_caps.is_int ? ac_caps.width : 16; - try.depth = ac_caps.is_int ? ac_caps.depth : 16; - try.endianness = ac_caps.is_int ? ac_caps.endianness : G_BYTE_ORDER; - } + //if (!GST_PAD_IS_NEGOTIATING (otherpad)) { + try.channels = 2; + try.width = 16; + try.depth = 16; + try.endianness = G_BYTE_ORDER; + /* + } else { + try.channels = ac_caps.channels; + try.width = ac_caps.is_int ? ac_caps.width : 16; + try.depth = ac_caps.is_int ? ac_caps.depth : 16; + try.endianness = ac_caps.is_int ? ac_caps.endianness : G_BYTE_ORDER; + } + */ if (_fixate_caps_to_int (©, "channels", try.channels)) { int n, c; @@ -591,7 +598,7 @@ gst_audio_convert_fixate (GstPad * pad, const GstCaps * caps) } } - gst_caps_free (copy); + gst_caps_unref (copy); return NULL; } @@ -639,7 +646,7 @@ gst_audio_convert_get_buffer (GstBuffer * buf, guint size) } else { ret = gst_buffer_new_and_alloc (size); g_assert (ret); - gst_buffer_stamp (ret, buf); + //gst_buffer_stamp (ret, buf); GST_LOG ("returning new buffer. data: %p - size: %u - maxsize: %u", ret->data, ret->size, ret->maxsize); return ret; @@ -690,6 +697,7 @@ gst_audio_convert_buffer_to_default_format (GstAudioConvert * this, ret = gst_audio_convert_get_buffer (buf, buf->size * 32 / this->sinkcaps.width); + gst_buffer_set_caps (ret, GST_RPAD_CAPS (this->src)); count = ret->size / 4; src = buf->data + (count - 1) * (this->sinkcaps.width / 8); @@ -757,6 +765,7 @@ gst_audio_convert_buffer_to_default_format (GstAudioConvert * this, /* should just give the same buffer, unless it's not writable -- float is * already 32 bits */ ret = gst_audio_convert_get_buffer (buf, buf->size); + gst_buffer_set_caps (ret, GST_RPAD_CAPS (this->src)); in = (gfloat *) GST_BUFFER_DATA (buf); out = (gint32 *) GST_BUFFER_DATA (ret); @@ -816,6 +825,7 @@ gst_audio_convert_buffer_from_default_format (GstAudioConvert * this, ret = gst_audio_convert_get_buffer (buf, buf->size * this->srccaps.width / 32); + gst_buffer_set_caps (ret, GST_RPAD_CAPS (this->src)); dest = ret->data; src = (gint32 *) buf->data; @@ -881,6 +891,7 @@ gst_audio_convert_buffer_from_default_format (GstAudioConvert * this, ret = gst_audio_convert_get_buffer (buf, buf->size * this->srccaps.width / 32); + gst_buffer_set_caps (ret, GST_RPAD_CAPS (this->src)); dest = (gfloat *) ret->data; src = (gint32 *) buf->data; @@ -902,6 +913,7 @@ gst_audio_convert_channels (GstAudioConvert * this, GstBuffer * buf) gint count; g_assert (this->matrix != NULL); + /* check for passthrough */ if (gst_audio_convert_passthrough (this)) return buf; @@ -909,6 +921,7 @@ gst_audio_convert_channels (GstAudioConvert * this, GstBuffer * buf) /* convert */ count = GST_BUFFER_SIZE (buf) / 4 / this->sinkcaps.channels; ret = gst_audio_convert_get_buffer (buf, count * 4 * this->srccaps.channels); + gst_buffer_set_caps (ret, GST_RPAD_CAPS (this->src)); gst_audio_convert_mix (this, (gint32 *) GST_BUFFER_DATA (buf), (gint32 *) GST_BUFFER_DATA (ret), count); gst_buffer_unref (buf); diff --git a/gst/audioconvert/gstchannelmix.h b/gst/audioconvert/gstchannelmix.h index 08a9c18377..68b09041a3 100644 --- a/gst/audioconvert/gstchannelmix.h +++ b/gst/audioconvert/gstchannelmix.h @@ -68,6 +68,9 @@ struct _GstAudioConvert GstAudioConvertCaps srccaps; GstAudioConvertCaps sinkcaps; + GstCaps *src_prefered; + GstCaps *sink_prefered; + /* channel conversion matrix, m[in_channels][out_channels]. * If identity matrix, passthrough applies. */ gfloat **matrix; diff --git a/gst/ffmpegcolorspace/avcodec.h b/gst/ffmpegcolorspace/avcodec.h index f9a39f9ac8..6846e3efbd 100644 --- a/gst/ffmpegcolorspace/avcodec.h +++ b/gst/ffmpegcolorspace/avcodec.h @@ -90,7 +90,6 @@ enum SampleFormat { #define DEFAULT_FRAME_RATE_BASE 1001000 - /* thomas: extracted from imgconvert.c since it's also used in * gstffmpegcodecmap.c */ diff --git a/gst/ffmpegcolorspace/gstffmpegcolorspace.c b/gst/ffmpegcolorspace/gstffmpegcolorspace.c index 37acbb6658..80b0732134 100644 --- a/gst/ffmpegcolorspace/gstffmpegcolorspace.c +++ b/gst/ffmpegcolorspace/gstffmpegcolorspace.c @@ -56,7 +56,9 @@ struct _GstFFMpegCsp enum PixelFormat from_pixfmt, to_pixfmt; AVPicture from_frame, to_frame; AVPaletteControl *palette; - GstCaps *sinkcaps; + + GstCaps *src_prefered; + GstCaps *sink_prefered; }; struct _GstFFMpegCspClass @@ -96,10 +98,7 @@ static void gst_ffmpegcsp_set_property (GObject * object, static void gst_ffmpegcsp_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); -static GstPadLinkReturn -gst_ffmpegcsp_pad_link (GstPad * pad, const GstCaps * caps); - -static void gst_ffmpegcsp_chain (GstPad * pad, GstData * data); +static GstFlowReturn gst_ffmpegcsp_chain (GstPad * pad, GstBuffer * buffer); static GstElementStateReturn gst_ffmpegcsp_change_state (GstElement * element); static GstPadTemplate *srctempl, *sinktempl; @@ -115,6 +114,8 @@ gst_ffmpegcsp_caps_remove_format_info (GstCaps * caps) GstStructure *structure; GstCaps *rgbcaps; + caps = gst_caps_make_writable (caps); + for (i = 0; i < gst_caps_get_size (caps); i++) { structure = gst_caps_get_structure (caps, i); @@ -151,47 +152,28 @@ gst_ffmpegcsp_getcaps (GstPad * pad) GstCaps *caps; GstPad *otherpad; - space = GST_FFMPEGCSP (gst_pad_get_parent (pad)); + space = GST_FFMPEGCSP (GST_PAD_PARENT (pad)); otherpad = (pad == space->srcpad) ? space->sinkpad : space->srcpad; - - othercaps = gst_pad_get_allowed_caps (otherpad); - + /* we can do whatever the peer can */ + othercaps = gst_pad_peer_get_caps (otherpad); + /* without the format info */ othercaps = gst_ffmpegcsp_caps_remove_format_info (othercaps); - + /* and filtered against our padtemplate */ caps = gst_caps_intersect (othercaps, gst_pad_get_pad_template_caps (pad)); - gst_caps_free (othercaps); + gst_caps_unref (othercaps); return caps; } -static GstPadLinkReturn -gst_ffmpegcsp_pad_link (GstPad * pad, const GstCaps * caps) +static gboolean +gst_ffmpegcsp_configure_context (GstPad * pad, const GstCaps * caps, gint width, + gint height) { - GstStructure *structure; AVCodecContext *ctx; GstFFMpegCsp *space; - const GstCaps *othercaps; - GstPad *otherpad; - GstPadLinkReturn ret; - int height, width; - double framerate; - const GValue *par = NULL; - space = GST_FFMPEGCSP (gst_pad_get_parent (pad)); - - GST_DEBUG_OBJECT (space, "pad_link on %s:%s with caps %" GST_PTR_FORMAT, - GST_DEBUG_PAD_NAME (pad), caps); - - otherpad = (pad == space->srcpad) ? space->sinkpad : space->srcpad; - - structure = gst_caps_get_structure (caps, 0); - gst_structure_get_int (structure, "width", &width); - gst_structure_get_int (structure, "height", &height); - gst_structure_get_double (structure, "framerate", &framerate); - par = gst_structure_get_value (structure, "pixel-aspect-ratio"); - - /* FIXME attempt and/or check for passthru */ + space = GST_FFMPEGCSP (GST_PAD_PARENT (pad)); /* loop over all possibilities and select the first one we can convert and * is accepted by the peer */ @@ -211,47 +193,97 @@ gst_ffmpegcsp_pad_link (GstPad * pad, const GstCaps * caps) space->from_pixfmt = PIX_FMT_NB; } - return GST_PAD_LINK_REFUSED; - } - - /* set the size on the otherpad */ - othercaps = gst_pad_get_negotiated_caps (otherpad); - if (othercaps) { - GstCaps *caps = gst_caps_copy (othercaps); - - gst_caps_set_simple (caps, - "width", G_TYPE_INT, width, - "height", G_TYPE_INT, height, - "framerate", G_TYPE_DOUBLE, framerate, NULL); - if (par) { - gst_caps_set_simple (caps, - "pixel-aspect-ratio", GST_TYPE_FRACTION, - gst_value_get_fraction_numerator (par), - gst_value_get_fraction_denominator (par), NULL); - } - ret = gst_pad_try_set_caps (otherpad, caps); - gst_caps_free (caps); - if (GST_PAD_LINK_FAILED (ret)) { - return ret; - } - } - - if (pad == space->srcpad) { - space->to_pixfmt = ctx->pix_fmt; + return FALSE; } else { - space->from_pixfmt = ctx->pix_fmt; + if (pad == space->srcpad) { + space->to_pixfmt = ctx->pix_fmt; + } else { + space->from_pixfmt = ctx->pix_fmt; - /* palette */ - if (space->palette) - av_free (space->palette); - space->palette = ctx->palctrl; + /* palette */ + if (space->palette) + av_free (space->palette); + space->palette = ctx->palctrl; + } + av_free (ctx); + } + return TRUE; +} + +/* configureing the caps on a pad means that we should check if we + * can get a fic format for that caps. Then we need to figure out + * how we can convert that to the peer format */ +static gboolean +gst_ffmpegcsp_setcaps (GstPad * pad, GstCaps * caps) +{ + GstStructure *structure; + GstFFMpegCsp *space; + GstPad *otherpeer; + GstPad *otherpad; + int height, width; + double framerate; + const GValue *par = NULL; + GstCaps **other_prefered, **prefered; + + space = GST_FFMPEGCSP (GST_PAD_PARENT (pad)); + + GST_DEBUG_OBJECT (space, "setcaps on %s:%s with caps %" GST_PTR_FORMAT, + GST_DEBUG_PAD_NAME (pad), caps); + + otherpad = (pad == space->srcpad) ? space->sinkpad : space->srcpad; + prefered = + (pad == space->srcpad) ? &space->src_prefered : &space->sink_prefered; + other_prefered = + (pad == space->srcpad) ? &space->sink_prefered : &space->src_prefered; + + structure = gst_caps_get_structure (caps, 0); + gst_structure_get_int (structure, "width", &width); + gst_structure_get_int (structure, "height", &height); + gst_structure_get_double (structure, "framerate", &framerate); + par = gst_structure_get_value (structure, "pixel-aspect-ratio"); + + if (!gst_ffmpegcsp_configure_context (pad, caps, width, height)) + return FALSE; + + *prefered = caps; + + otherpeer = gst_pad_get_peer (otherpad); + if (otherpeer) { + /* check passthrough */ + //if (gst_pad_accept_caps (otherpeer, caps)) { + if (FALSE) { + *other_prefered = gst_caps_ref (caps); + } else { + GstCaps *othercaps; + + /* set the size on the otherpad */ + othercaps = gst_pad_get_caps (otherpeer); + if (othercaps) { + GstCaps *targetcaps = gst_caps_copy_nth (othercaps, 0); + + gst_caps_unref (othercaps); + + gst_caps_set_simple (targetcaps, + "width", G_TYPE_INT, width, + "height", G_TYPE_INT, height, + "framerate", G_TYPE_DOUBLE, framerate, NULL); + + if (par) { + gst_caps_set_simple (targetcaps, + "pixel-aspect-ratio", GST_TYPE_FRACTION, + gst_value_get_fraction_numerator (par), + gst_value_get_fraction_denominator (par), NULL); + } + *other_prefered = targetcaps; + } + } + gst_object_unref (GST_OBJECT (otherpeer)); } - av_free (ctx); space->width = width; space->height = height; - return GST_PAD_LINK_OK; + return TRUE; } static GType @@ -313,62 +345,63 @@ static void gst_ffmpegcsp_init (GstFFMpegCsp * space) { space->sinkpad = gst_pad_new_from_template (sinktempl, "sink"); - gst_pad_set_link_function (space->sinkpad, gst_ffmpegcsp_pad_link); gst_pad_set_getcaps_function (space->sinkpad, gst_ffmpegcsp_getcaps); + gst_pad_set_setcaps_function (space->sinkpad, gst_ffmpegcsp_setcaps); gst_pad_set_chain_function (space->sinkpad, gst_ffmpegcsp_chain); gst_element_add_pad (GST_ELEMENT (space), space->sinkpad); space->srcpad = gst_pad_new_from_template (srctempl, "src"); gst_element_add_pad (GST_ELEMENT (space), space->srcpad); - gst_pad_set_link_function (space->srcpad, gst_ffmpegcsp_pad_link); gst_pad_set_getcaps_function (space->srcpad, gst_ffmpegcsp_getcaps); + gst_pad_set_setcaps_function (space->srcpad, gst_ffmpegcsp_setcaps); space->from_pixfmt = space->to_pixfmt = PIX_FMT_NB; space->palette = NULL; } -static void -gst_ffmpegcsp_chain (GstPad * pad, GstData * data) +static GstFlowReturn +gst_ffmpegcsp_chain (GstPad * pad, GstBuffer * buffer) { - GstBuffer *inbuf = GST_BUFFER (data); + GstBuffer *inbuf = GST_BUFFER (buffer); GstFFMpegCsp *space; GstBuffer *outbuf = NULL; - g_return_if_fail (pad != NULL); - g_return_if_fail (GST_IS_PAD (pad)); - g_return_if_fail (inbuf != NULL); + //GstCaps *outcaps; - space = GST_FFMPEGCSP (gst_pad_get_parent (pad)); - - g_return_if_fail (space != NULL); - g_return_if_fail (GST_IS_FFMPEGCSP (space)); + space = GST_FFMPEGCSP (GST_PAD_PARENT (pad)); if (!GST_PAD_IS_USABLE (space->srcpad)) { gst_buffer_unref (inbuf); - return; + return GST_FLOW_ERROR; + } + + /* asume passthrough */ + guint size = + avpicture_get_size (space->from_pixfmt, space->width, space->height); + + outbuf = gst_pad_alloc_buffer (space->srcpad, GST_BUFFER_OFFSET_NONE, size, + space->src_prefered); + if (outbuf == NULL) { + return GST_FLOW_ERROR; } if (space->from_pixfmt == PIX_FMT_NB || space->to_pixfmt == PIX_FMT_NB) { GST_ELEMENT_ERROR (space, CORE, NOT_IMPLEMENTED, (NULL), ("attempting to convert colorspaces between unknown formats")); gst_buffer_unref (inbuf); - return; + return GST_FLOW_NOT_NEGOTIATED; } if (space->from_pixfmt == space->to_pixfmt) { outbuf = inbuf; + gst_buffer_unref (outbuf); } else { - guint size = avpicture_get_size (space->to_pixfmt, - space->width, space->height); - - outbuf = gst_pad_alloc_buffer (space->srcpad, GST_BUFFER_OFFSET_NONE, size); - /* convert */ gst_ffmpegcsp_avpicture_fill (&space->from_frame, GST_BUFFER_DATA (inbuf), space->from_pixfmt, space->width, space->height); if (space->palette) - space->from_frame.data[1] = (uint8_t *) space->palette->palette; + space->from_frame.data[1] = (uint8_t *) space->palette; gst_ffmpegcsp_avpicture_fill (&space->to_frame, GST_BUFFER_DATA (outbuf), space->to_pixfmt, space->width, space->height); @@ -377,16 +410,10 @@ gst_ffmpegcsp_chain (GstPad * pad, GstData * data) GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (inbuf); GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (inbuf); - gst_buffer_unref (inbuf); } - /* try negotiating here if it isn't yet */ - if (!gst_pad_is_negotiated (space->srcpad)) { - gst_pad_renegotiate (space->srcpad); - } - - gst_pad_push (space->srcpad, GST_DATA (outbuf)); + return gst_pad_push (space->srcpad, outbuf); } static GstElementStateReturn diff --git a/gst/tags/gstid3tag.c b/gst/tags/gstid3tag.c index e88932bf16..f34d1c4b29 100644 --- a/gst/tags/gstid3tag.c +++ b/gst/tags/gstid3tag.c @@ -279,7 +279,6 @@ gst_tag_extract_id3v1_string (GstTagList * list, const gchar * tag, } } } - /* Try current locale (if not UTF-8) */ if (!g_get_charset (&env)) { if ((utf8 = g_locale_to_utf8 (start, size, &bytes_read, NULL, NULL))) { diff --git a/gst/tags/gstvorbistag.c b/gst/tags/gstvorbistag.c index 52c1248831..c0736e584b 100644 --- a/gst/tags/gstvorbistag.c +++ b/gst/tags/gstvorbistag.c @@ -96,7 +96,7 @@ static void gst_vorbis_tag_base_init (gpointer g_class); static void gst_vorbis_tag_class_init (gpointer g_class, gpointer class_data); static void gst_vorbis_tag_init (GTypeInstance * instance, gpointer g_class); -static void gst_vorbis_tag_chain (GstPad * pad, GstData * data); +static GstFlowReturn gst_vorbis_tag_chain (GstPad * pad, GstBuffer * buffer); static GstElementStateReturn gst_vorbis_tag_change_state (GstElement * element); @@ -571,13 +571,13 @@ gst_tag_list_to_vorbiscomment_buffer (const GstTagList * list, return buffer; } -static void -gst_vorbis_tag_chain (GstPad * pad, GstData * data) + +static GstFlowReturn +gst_vorbis_tag_chain (GstPad * pad, GstBuffer * buffer) { GstVorbisTag *tag; - GstBuffer *buffer; + GstBuffer *out = NULL; - buffer = GST_BUFFER (data); tag = GST_VORBIS_TAG (gst_pad_get_parent (pad)); if (tag->output == OUTPUT_UNKNOWN) { @@ -586,6 +586,7 @@ gst_vorbis_tag_chain (GstPad * pad, GstData * data) /* caps nego */ do { +#if 0 if (gst_pad_try_set_caps (tag->srcpad, vorbis_caps) >= 0) { tag->output = OUTPUT_DATA; } else if (gst_pad_try_set_caps (tag->srcpad, tags_caps) >= 0) { @@ -595,14 +596,15 @@ gst_vorbis_tag_chain (GstPad * pad, GstData * data) gst_static_caps_get (&gst_vorbis_tag_src_template.static_caps); if (gst_pad_recover_caps_error (tag->srcpad, caps)) continue; - gst_caps_free (vorbis_caps); - gst_caps_free (tags_caps); - return; + gst_caps_unref (vorbis_caps); + gst_caps_unref (tags_caps); + return GST_FLOW_ERROR; } +#endif } while (FALSE); - gst_caps_free (vorbis_caps); - gst_caps_free (tags_caps); + gst_caps_unref (vorbis_caps); + gst_caps_unref (tags_caps); } if (GST_BUFFER_SIZE (buffer) == 0) @@ -616,31 +618,32 @@ gst_vorbis_tag_chain (GstPad * pad, GstData * data) &vendor); const GstTagList *found_tags; - gst_data_unref (data); + gst_data_unref (GST_DATA (buffer)); if (list == NULL) { GST_ELEMENT_ERROR (tag, CORE, TAG, (NULL), ("invalid data in vorbis comments")); - return; + return GST_FLOW_ERROR; } - gst_element_found_tags_for_pad (GST_ELEMENT (tag), tag->srcpad, 0, - gst_tag_list_copy (list)); + //gst_element_found_tags_for_pad (GST_ELEMENT (tag), tag->srcpad, 0, + // gst_tag_list_copy (list)); + found_tags = gst_tag_setter_get_list (GST_TAG_SETTER (tag)); if (found_tags) gst_tag_list_insert (list, found_tags, gst_tag_setter_get_merge_mode (GST_TAG_SETTER (tag))); - data = - GST_DATA (gst_tag_list_to_vorbiscomment_buffer (list, "\003vorbis", 7, - vendor)); + out = gst_tag_list_to_vorbiscomment_buffer (list, "\003vorbis", 7, vendor); gst_tag_list_free (list); g_free (vendor); } if (tag->output == OUTPUT_DATA) { - gst_pad_push (tag->srcpad, data); + gst_pad_push (tag->srcpad, out); } else { - gst_data_unref (data); + gst_data_unref (GST_DATA (out)); } + return GST_FLOW_OK; } + static GstElementStateReturn gst_vorbis_tag_change_state (GstElement * element) { diff --git a/gst/typefind/gsttypefindfunctions.c b/gst/typefind/gsttypefindfunctions.c index 7c6fda3cb8..c7ef5676b1 100644 --- a/gst/typefind/gsttypefindfunctions.c +++ b/gst/typefind/gsttypefindfunctions.c @@ -256,7 +256,7 @@ aac_type_find (GstTypeFind * tf, gpointer unused) NULL); gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, caps); - gst_caps_free (caps); + gst_caps_unref (caps); } } } @@ -496,7 +496,7 @@ mp3_type_find (GstTypeFind * tf, gpointer unused) gst_structure_set (gst_caps_get_structure (caps, 0), "layer", G_TYPE_INT, layer, 0); gst_type_find_suggest (tf, probability, caps); - gst_caps_free (caps); + gst_caps_unref (caps); } return; } @@ -570,7 +570,7 @@ mpeg2_sys_type_find (GstTypeFind * tf, gpointer unused) gst_structure_set (gst_caps_get_structure (caps, 0), "mpegversion", G_TYPE_INT, 1, 0); gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, caps); - gst_caps_free (caps); + gst_caps_unref (caps); } } else if (data && IS_MPEG_PES_HEADER (data)) { /* PES stream */ @@ -712,7 +712,7 @@ mpeg1_sys_type_find (GstTypeFind * tf, gpointer unused) gst_structure_set (gst_caps_get_structure (caps, 0), "mpegversion", G_TYPE_INT, 1, 0); gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM - 1, caps); - gst_caps_free (caps); + gst_caps_unref (caps); return; } } @@ -741,7 +741,7 @@ mpeg_video_type_find (GstTypeFind * tf, gpointer unused) gst_structure_set (gst_caps_get_structure (caps, 0), "mpegversion", G_TYPE_INT, 1, 0); gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM - 1, caps); - gst_caps_free (caps); + gst_caps_unref (caps); } } @@ -772,7 +772,7 @@ mpeg_video_stream_type_find (GstTypeFind * tf, gpointer unused) gst_structure_set (gst_caps_get_structure (caps, 0), "mpegversion", G_TYPE_INT, 1, 0); gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM - 2, caps); - gst_caps_free (caps); + gst_caps_unref (caps); return; } @@ -989,7 +989,6 @@ q3gp_type_find (GstTypeFind * tf, gpointer unused) static GstStaticCaps mod_caps = GST_STATIC_CAPS ("audio/x-mod"); - #define MOD_CAPS gst_static_caps_get(&mod_caps) /* FIXME: M15 CheckType to do */ static void @@ -1319,7 +1318,7 @@ dv_type_find (GstTypeFind * tf, gpointer private) G_TYPE_STRING, format, NULL); gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, caps); - gst_caps_free (caps); + gst_caps_unref (caps); } } diff --git a/gst/videotestsrc/gstvideotestsrc.c b/gst/videotestsrc/gstvideotestsrc.c index 2929014d4e..fd06e0eaf2 100644 --- a/gst/videotestsrc/gstvideotestsrc.c +++ b/gst/videotestsrc/gstvideotestsrc.c @@ -71,7 +71,7 @@ static void gst_videotestsrc_set_property (GObject * object, guint prop_id, static void gst_videotestsrc_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); -static GstData *gst_videotestsrc_get (GstPad * pad); +static void gst_videotestsrc_loop (GstPad * pad); static const GstQueryType *gst_videotestsrc_get_query_types (GstPad * pad); static gboolean gst_videotestsrc_src_query (GstPad * pad, @@ -160,6 +160,9 @@ gst_videotestsrc_class_init (GstVideotestsrcClass * klass) gobject_class = (GObjectClass *) klass; gstelement_class = (GstElementClass *) klass; + gobject_class->set_property = gst_videotestsrc_set_property; + gobject_class->get_property = gst_videotestsrc_get_property; + g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TYPE, g_param_spec_enum ("pattern", "Pattern", "Type of test pattern to generate", GST_TYPE_VIDEOTESTSRC_PATTERN, 1, @@ -174,9 +177,6 @@ gst_videotestsrc_class_init (GstVideotestsrcClass * klass) parent_class = g_type_class_ref (GST_TYPE_ELEMENT); - gobject_class->set_property = gst_videotestsrc_set_property; - gobject_class->get_property = gst_videotestsrc_get_property; - gstelement_class->change_state = gst_videotestsrc_change_state; gstelement_class->set_clock = gst_videotestsrc_set_clock; @@ -195,66 +195,107 @@ gst_videotestsrc_set_clock (GstElement * element, GstClock * clock) } static GstCaps * -gst_videotestsrc_src_fixate (GstPad * pad, const GstCaps * caps) +gst_videotestsrc_src_negotiate (GstPad * pad) { - GstStructure *structure; - GstCaps *newcaps; + GstCaps *caps; + GstCaps *temp; - /* FIXME this function isn't very intelligent in choosing "good" caps */ + /* get all possible caps on this link */ + caps = gst_pad_get_allowed_caps (pad); + temp = gst_caps_normalize (caps); + gst_caps_unref (caps); + caps = temp; - if (gst_caps_get_size (caps) > 1) - return NULL; + if (gst_caps_get_size (caps) > 0) { + GstStructure *structure; - newcaps = gst_caps_copy (caps); - structure = gst_caps_get_structure (newcaps, 0); + /* pick the first one */ + temp = gst_caps_copy_nth (caps, 0); + gst_caps_unref (caps); + caps = temp; - if (gst_caps_structure_fixate_field_nearest_int (structure, "width", 320)) { - return newcaps; - } - if (gst_caps_structure_fixate_field_nearest_int (structure, "height", 240)) { - return newcaps; - } - if (gst_caps_structure_fixate_field_nearest_double (structure, "framerate", - 30.0)) { - return newcaps; + structure = gst_caps_get_structure (caps, 0); + + gst_caps_structure_fixate_field_nearest_int (structure, "width", 320); + gst_caps_structure_fixate_field_nearest_int (structure, "height", 240); + gst_caps_structure_fixate_field_nearest_double (structure, "framerate", + 30.0); } - /* failed to fixate */ - gst_caps_free (newcaps); - return NULL; + return caps; } static GstPadLinkReturn -gst_videotestsrc_src_link (GstPad * pad, const GstCaps * caps) +gst_videotestsrc_src_link (GstPad * pad, GstPad * peer) +{ + return GST_PAD_LINK_OK; +} + +static gboolean +gst_videotestsrc_parse_caps (const GstCaps * caps, + gint * width, gint * height, gdouble * rate, + struct fourcc_list_struct **fourcc) { - GstVideotestsrc *videotestsrc; const GstStructure *structure; GstPadLinkReturn ret; - videotestsrc = GST_VIDEOTESTSRC (gst_pad_get_parent (pad)); - GST_DEBUG_OBJECT (videotestsrc, "linking"); + GST_DEBUG ("parsing caps"); + + if (gst_caps_get_size (caps) < 1) + return FALSE; structure = gst_caps_get_structure (caps, 0); - videotestsrc->fourcc = paintinfo_find_by_structure (structure); - if (!videotestsrc->fourcc) { + *fourcc = paintinfo_find_by_structure (structure); + if (!*fourcc) { g_critical ("videotestsrc format not found"); - return GST_PAD_LINK_REFUSED; + return FALSE; } - ret = gst_structure_get_int (structure, "width", &videotestsrc->width); - ret &= gst_structure_get_int (structure, "height", &videotestsrc->height); - ret &= gst_structure_get_double (structure, "framerate", &videotestsrc->rate); + ret = gst_structure_get_int (structure, "width", width); + ret &= gst_structure_get_int (structure, "height", height); + ret &= gst_structure_get_double (structure, "framerate", rate); - if (!ret) - return GST_PAD_LINK_REFUSED; + return ret; +} - videotestsrc->bpp = videotestsrc->fourcc->bitspp; +#if 0 +static gboolean +gst_videotestsrc_src_accept_caps (GstPad * pad, const GstCaps * caps) +{ + gint width, height; + gdouble rate; + struct fourcc_list_struct *fourcc; - GST_DEBUG_OBJECT (videotestsrc, "size %dx%d, %f fps", videotestsrc->width, - videotestsrc->height, videotestsrc->rate); + return gst_videotestsrc_parse_caps (caps, &width, &height, &rate, &fourcc); +} +#endif - return GST_PAD_LINK_OK; +static gboolean +gst_videotestsrc_setcaps (GstPad * pad, GstCaps * caps) +{ + gboolean res; + gint width, height; + gdouble rate; + struct fourcc_list_struct *fourcc; + GstVideotestsrc *videotestsrc; + + videotestsrc = GST_VIDEOTESTSRC (GST_PAD_PARENT (pad)); + + res = gst_videotestsrc_parse_caps (caps, &width, &height, &rate, &fourcc); + if (res) { + /* looks ok here */ + videotestsrc->fourcc = fourcc; + videotestsrc->width = width; + videotestsrc->height = height; + videotestsrc->rate = rate; + videotestsrc->bpp = videotestsrc->fourcc->bitspp; + + GST_DEBUG_OBJECT (videotestsrc, "size %dx%d, %f fps", videotestsrc->width, + videotestsrc->height, videotestsrc->rate); + } + + return res; } static void @@ -262,13 +303,55 @@ gst_videotestsrc_src_unlink (GstPad * pad) { GstVideotestsrc *videotestsrc; - videotestsrc = GST_VIDEOTESTSRC (gst_pad_get_parent (pad)); + videotestsrc = GST_VIDEOTESTSRC (GST_PAD_PARENT (pad)); } +static gboolean +gst_videotestsrc_activate (GstPad * pad, GstActivateMode mode) +{ + gboolean result = FALSE; + GstVideotestsrc *videotestsrc; + + videotestsrc = GST_VIDEOTESTSRC (GST_OBJECT_PARENT (pad)); + + switch (mode) { + case GST_ACTIVATE_PULL: + break; + case GST_ACTIVATE_PUSH: + /* if we have a scheduler we can start the task */ + if (GST_ELEMENT_SCHEDULER (videotestsrc)) { + GST_STREAM_LOCK (pad); + GST_RPAD_TASK (pad) = + gst_scheduler_create_task (GST_ELEMENT_SCHEDULER (videotestsrc), + (GstTaskFunction) gst_videotestsrc_loop, pad); + + gst_task_start (GST_RPAD_TASK (pad)); + GST_STREAM_UNLOCK (pad); + result = TRUE; + } + break; + case GST_ACTIVATE_NONE: + /* step 1, unblock clock sync (if any) */ + + /* step 2, make sure streaming finishes */ + GST_STREAM_LOCK (pad); + /* step 3, stop the task */ + gst_task_stop (GST_RPAD_TASK (pad)); + gst_object_unref (GST_OBJECT (GST_RPAD_TASK (pad))); + GST_STREAM_UNLOCK (pad); + + result = TRUE; + break; + } + return result; +} + + static GstElementStateReturn gst_videotestsrc_change_state (GstElement * element) { GstVideotestsrc *videotestsrc; + GstElementStateReturn result; videotestsrc = GST_VIDEOTESTSRC (element); @@ -280,6 +363,11 @@ gst_videotestsrc_change_state (GstElement * element) break; case GST_STATE_PAUSED_TO_PLAYING: break; + } + + result = parent_class->change_state (element); + + switch (GST_STATE_TRANSITION (element)) { case GST_STATE_PLAYING_TO_PAUSED: break; case GST_STATE_PAUSED_TO_READY: @@ -291,7 +379,7 @@ gst_videotestsrc_change_state (GstElement * element) break; } - return parent_class->change_state (element); + return result; } static GstCaps * @@ -348,7 +436,7 @@ gst_videotestsrc_getcaps (GstPad * pad) { GstVideotestsrc *vts; - vts = GST_VIDEOTESTSRC (gst_pad_get_parent (pad)); + vts = GST_VIDEOTESTSRC (GST_PAD_PARENT (pad)); return gst_videotestsrc_get_capslist (); } @@ -360,10 +448,11 @@ gst_videotestsrc_init (GstVideotestsrc * videotestsrc) gst_pad_new_from_template (gst_videotestsrc_src_template_factory (), "src"); gst_pad_set_getcaps_function (videotestsrc->srcpad, gst_videotestsrc_getcaps); - gst_pad_set_fixate_function (videotestsrc->srcpad, - gst_videotestsrc_src_fixate); + gst_pad_set_setcaps_function (videotestsrc->srcpad, gst_videotestsrc_setcaps); gst_element_add_pad (GST_ELEMENT (videotestsrc), videotestsrc->srcpad); - gst_pad_set_get_function (videotestsrc->srcpad, gst_videotestsrc_get); + gst_pad_set_activate_function (videotestsrc->srcpad, + gst_videotestsrc_activate); + gst_pad_set_loop_function (videotestsrc->srcpad, gst_videotestsrc_loop); gst_pad_set_link_function (videotestsrc->srcpad, gst_videotestsrc_src_link); gst_pad_set_unlink_function (videotestsrc->srcpad, gst_videotestsrc_src_unlink); @@ -405,7 +494,7 @@ gst_videotestsrc_src_query (GstPad * pad, GstQueryType type, GstFormat * format, gint64 * value) { gboolean res = FALSE; - GstVideotestsrc *videotestsrc = GST_VIDEOTESTSRC (gst_pad_get_parent (pad)); + GstVideotestsrc *videotestsrc = GST_VIDEOTESTSRC (GST_PAD_PARENT (pad)); switch (type) { case GST_QUERY_POSITION: @@ -446,42 +535,15 @@ gst_videotestsrc_handle_src_event (GstPad * pad, GstEvent * event) gboolean res = TRUE; GstVideotestsrc *videotestsrc; gint64 new_n_frames; - GstClockTime new_running_time; g_return_val_if_fail (pad != NULL, FALSE); g_return_val_if_fail (GST_IS_PAD (pad), FALSE); - videotestsrc = GST_VIDEOTESTSRC (gst_pad_get_parent (pad)); + videotestsrc = GST_VIDEOTESTSRC (GST_PAD_PARENT (pad)); new_n_frames = videotestsrc->n_frames; switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEEK: - { - /* since we don't do bookkeeping, we just assign a useful meaning - * to the time/frame relation after seeking based on current fps */ - switch (GST_EVENT_SEEK_FORMAT (event)) { - case GST_FORMAT_TIME: - new_running_time = GST_EVENT_SEEK_OFFSET (event); - new_n_frames = - GST_EVENT_SEEK_OFFSET (event) * (double) videotestsrc->rate / - GST_SECOND; - videotestsrc->segment_start_frame = -1; - videotestsrc->segment_end_frame = -1; - break; - case GST_FORMAT_DEFAULT: - new_n_frames = GST_EVENT_SEEK_OFFSET (event); - new_running_time = GST_EVENT_SEEK_OFFSET (event) / - (double) videotestsrc->rate * GST_SECOND; - videotestsrc->segment_start_frame = -1; - videotestsrc->segment_end_frame = -1; - break; - default: - res = FALSE; - break; - } - break; - } - case GST_EVENT_SEEK_SEGMENT: { switch (GST_EVENT_SEEK_FORMAT (event)) { case GST_FORMAT_TIME: @@ -517,27 +579,39 @@ gst_videotestsrc_handle_src_event (GstPad * pad, GstEvent * event) videotestsrc->n_frames = new_n_frames; videotestsrc->need_discont = TRUE; } + gst_event_unref (event); return res; } -static GstData * -gst_videotestsrc_get (GstPad * pad) +static void +gst_videotestsrc_loop (GstPad * pad) { GstVideotestsrc *videotestsrc; gulong newsize; - GstBuffer *buf; + GstBuffer *outbuf; - g_return_val_if_fail (pad != NULL, NULL); - g_return_val_if_fail (GST_IS_PAD (pad), NULL); - - videotestsrc = GST_VIDEOTESTSRC (gst_pad_get_parent (pad)); + videotestsrc = GST_VIDEOTESTSRC (GST_PAD_PARENT (pad)); GST_LOG_OBJECT (videotestsrc, "get"); + /* no format on this pad, find a format */ + if (GST_PAD_CAPS (pad) == NULL) { + GstCaps *caps; + + caps = gst_videotestsrc_src_negotiate (pad); + if (!gst_pad_set_caps (videotestsrc->srcpad, caps)) { + GST_ELEMENT_ERROR (videotestsrc, CORE, NEGOTIATION, (NULL), + ("format could not be negotiated")); + gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS)); + goto need_pause; + } + } + if (videotestsrc->fourcc == NULL) { GST_ELEMENT_ERROR (videotestsrc, CORE, NEGOTIATION, (NULL), ("format wasn't negotiated before get function")); - return NULL; + gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS)); + goto need_pause; } if (videotestsrc->need_discont) { @@ -545,23 +619,23 @@ gst_videotestsrc_get (GstPad * pad) (videotestsrc->n_frames * GST_SECOND) / (double) videotestsrc->rate; videotestsrc->need_discont = FALSE; - return GST_DATA (gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME, ts, - NULL)); + gst_pad_push_event (pad, gst_event_new_discontinuous (FALSE, + GST_FORMAT_TIME, ts, NULL)); } if ((videotestsrc->segment_end_frame != -1) && (videotestsrc->n_frames > videotestsrc->segment_end_frame)) { if (videotestsrc->loop) { - return GST_DATA (gst_event_new (GST_EVENT_SEGMENT_DONE)); + //gst_pad_push_event (pad, gst_event_new (GST_EVENT_SEGMENT_DONE)); } else { - gst_element_set_eos (GST_ELEMENT (videotestsrc)); - return GST_DATA (gst_event_new (GST_EVENT_EOS)); + gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS)); + goto need_pause; } } if (videotestsrc->num_buffers_left == 0) { - gst_element_set_eos (GST_ELEMENT (videotestsrc)); - return GST_DATA (gst_event_new (GST_EVENT_EOS)); + gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS)); + goto need_pause; } else { if (videotestsrc->num_buffers_left > 0) videotestsrc->num_buffers_left--; @@ -569,29 +643,46 @@ gst_videotestsrc_get (GstPad * pad) newsize = gst_videotestsrc_get_size (videotestsrc, videotestsrc->width, videotestsrc->height); - g_return_val_if_fail (newsize > 0, NULL); + g_return_if_fail (newsize > 0); GST_LOG_OBJECT (videotestsrc, "creating buffer of %ld bytes for %dx%d image", newsize, videotestsrc->width, videotestsrc->height); - buf = gst_pad_alloc_buffer (pad, GST_BUFFER_OFFSET_NONE, newsize); - g_return_val_if_fail (GST_BUFFER_DATA (buf) != NULL, NULL); + outbuf = + gst_pad_alloc_buffer (pad, GST_BUFFER_OFFSET_NONE, newsize, + GST_RPAD_CAPS (pad)); - videotestsrc->make_image (videotestsrc, (void *) GST_BUFFER_DATA (buf), + if (GST_BUFFER_CAPS (outbuf) != GST_PAD_CAPS (pad)) { + if (!gst_pad_set_caps (pad, GST_BUFFER_CAPS (outbuf))) { + GST_ELEMENT_ERROR (videotestsrc, CORE, NEGOTIATION, (NULL), + ("format wasn't accepted")); + gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS)); + goto need_pause; + } + } + + videotestsrc->make_image (videotestsrc, (void *) GST_BUFFER_DATA (outbuf), videotestsrc->width, videotestsrc->height); - GST_BUFFER_TIMESTAMP (buf) = videotestsrc->timestamp_offset + + GST_BUFFER_TIMESTAMP (outbuf) = videotestsrc->timestamp_offset + videotestsrc->running_time; - GST_BUFFER_DURATION (buf) = GST_SECOND / (double) videotestsrc->rate; + GST_BUFFER_DURATION (outbuf) = GST_SECOND / (double) videotestsrc->rate; if (videotestsrc->sync) { /* FIXME this is not correct if we do QoS */ if (videotestsrc->clock) { - gst_element_wait (GST_ELEMENT (videotestsrc), GST_BUFFER_TIMESTAMP (buf)); + //gst_element_wait (GST_ELEMENT (videotestsrc), GST_BUFFER_TIMESTAMP (outbuf)); } } videotestsrc->n_frames++; - videotestsrc->running_time += GST_BUFFER_DURATION (buf); - return GST_DATA (buf); + videotestsrc->running_time += GST_BUFFER_DURATION (outbuf); + + gst_pad_push (pad, outbuf); + return; + +need_pause: + { + gst_task_pause (GST_RPAD_TASK (pad)); + } } static void diff --git a/sys/xvimage/xvimagesink.c b/sys/xvimage/xvimagesink.c index dcc9d2e985..cc942e9f03 100644 --- a/sys/xvimage/xvimagesink.c +++ b/sys/xvimage/xvimagesink.c @@ -49,8 +49,7 @@ MotifWmHints, MwmHints; static void gst_xvimagesink_buffer_free (GstBuffer * buffer); static void gst_xvimagesink_xvimage_destroy (GstXvImageSink * xvimagesink, GstXvImage * xvimage); -static void -gst_xvimagesink_send_pending_navigation (GstXvImageSink * xvimagesink); +//static void gst_xvimagesink_send_pending_navigation (GstXvImageSink * xvimagesink); /* ElementFactory information */ static GstElementDetails gst_xvimagesink_details = @@ -821,7 +820,7 @@ gst_xvimagesink_get_xv_support (GstXvImageSink * xvimagesink, GST_DEBUG ("Generated the following caps: %" GST_PTR_FORMAT, caps); if (gst_caps_is_empty (caps)) { - gst_caps_free (caps); + gst_caps_unref (caps); XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0); GST_ELEMENT_ERROR (xvimagesink, STREAM, WRONG_TYPE, (NULL), ("No supported format found")); @@ -1066,7 +1065,7 @@ gst_xvimagesink_xcontext_clear (GstXvImageSink * xvimagesink) while (formats_list) { GstXvImageFormat *format = formats_list->data; - gst_caps_free (format->caps); + gst_caps_unref (format->caps); g_free (format); formats_list = g_list_next (formats_list); } @@ -1086,7 +1085,7 @@ gst_xvimagesink_xcontext_clear (GstXvImageSink * xvimagesink) if (xvimagesink->xcontext->channels_list) g_list_free (xvimagesink->xcontext->channels_list); - gst_caps_free (xvimagesink->xcontext->caps); + gst_caps_unref (xvimagesink->xcontext->caps); g_free (xvimagesink->xcontext->par); g_mutex_lock (xvimagesink->x_lock); @@ -1120,6 +1119,7 @@ gst_xvimagesink_imagepool_clear (GstXvImageSink * xvimagesink) /* Element stuff */ +#if 0 static GstCaps * gst_xvimagesink_fixate (GstPad * pad, const GstCaps * caps) { @@ -1143,9 +1143,10 @@ gst_xvimagesink_fixate (GstPad * pad, const GstCaps * caps) return newcaps; } - gst_caps_free (newcaps); + gst_caps_unref (newcaps); return NULL; } +#endif /* This function tries to get a format matching with a given caps in the supported list of formats we generated in gst_xvimagesink_get_xv_support */ @@ -1176,20 +1177,24 @@ gst_xvimagesink_get_fourcc_from_caps (GstXvImageSink * xvimagesink, } static GstCaps * -gst_xvimagesink_getcaps (GstPad * pad) +gst_xvimagesink_getcaps (GstBaseSink * bsink) { GstXvImageSink *xvimagesink; - xvimagesink = GST_XVIMAGESINK (gst_pad_get_parent (pad)); + xvimagesink = GST_XVIMAGESINK (bsink); + + g_print ("getcaps\n"); if (xvimagesink->xcontext) - return gst_caps_copy (xvimagesink->xcontext->caps); + return gst_caps_ref (xvimagesink->xcontext->caps); - return gst_caps_copy (gst_pad_get_pad_template_caps (pad)); + return + gst_caps_copy (gst_pad_get_pad_template_caps (GST_VIDEOSINK_PAD + (xvimagesink))); } -static GstPadLinkReturn -gst_xvimagesink_sink_link (GstPad * pad, const GstCaps * caps) +static gboolean +gst_xvimagesink_setcaps (GstBaseSink * bsink, GstCaps * caps) { GstXvImageSink *xvimagesink; GstStructure *structure; @@ -1202,10 +1207,7 @@ gst_xvimagesink_sink_link (GstPad * pad, const GstCaps * caps) const GValue *caps_par; gint num, den; - xvimagesink = GST_XVIMAGESINK (gst_pad_get_parent (pad)); - - if (!xvimagesink->xcontext) - return GST_PAD_LINK_DELAYED; + xvimagesink = GST_XVIMAGESINK (bsink); GST_DEBUG_OBJECT (xvimagesink, "sinkconnect possible caps %" GST_PTR_FORMAT " with given caps %" @@ -1217,9 +1219,7 @@ gst_xvimagesink_sink_link (GstPad * pad, const GstCaps * caps) ret &= gst_structure_get_double (structure, "framerate", &xvimagesink->framerate); if (!ret) - return GST_PAD_LINK_REFUSED; - - g_mutex_lock (xvimagesink->stream_lock); + return FALSE; xvimagesink->video_width = video_width; xvimagesink->video_height = video_height; @@ -1229,8 +1229,7 @@ gst_xvimagesink_sink_link (GstPad * pad, const GstCaps * caps) gst_caps_copy (caps)); } if (im_format == 0) { - g_mutex_unlock (xvimagesink->stream_lock); - return GST_PAD_LINK_REFUSED; + return FALSE; } /* get aspect ratio from caps if it's present, and @@ -1319,12 +1318,10 @@ gst_xvimagesink_sink_link (GstPad * pad, const GstCaps * caps) xvimagesink->xcontext->im_format = im_format; - g_mutex_unlock (xvimagesink->stream_lock); - gst_x_overlay_got_desired_size (GST_X_OVERLAY (xvimagesink), GST_VIDEOSINK_WIDTH (xvimagesink), GST_VIDEOSINK_HEIGHT (xvimagesink)); - return GST_PAD_LINK_OK; + return TRUE; } static GstElementStateReturn @@ -1349,32 +1346,23 @@ gst_xvimagesink_change_state (GstElement * element) /* call XSynchronize with the current value of synchronous */ GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s", xvimagesink->synchronous ? "TRUE" : "FALSE"); - g_mutex_lock (xvimagesink->x_lock); XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous); - g_mutex_unlock (xvimagesink->x_lock); gst_xvimagesink_update_colorbalance (xvimagesink); break; case GST_STATE_READY_TO_PAUSED: - xvimagesink->time = 0; + if (xvimagesink->xwindow) + gst_xvimagesink_xwindow_clear (xvimagesink, xvimagesink->xwindow); break; case GST_STATE_PAUSED_TO_PLAYING: break; case GST_STATE_PLAYING_TO_PAUSED: break; case GST_STATE_PAUSED_TO_READY: - g_mutex_lock (xvimagesink->stream_lock); - if (xvimagesink->xwindow) - gst_xvimagesink_xwindow_clear (xvimagesink, xvimagesink->xwindow); xvimagesink->framerate = 0; GST_VIDEOSINK_WIDTH (xvimagesink) = 0; GST_VIDEOSINK_HEIGHT (xvimagesink) = 0; - g_mutex_unlock (xvimagesink->stream_lock); break; case GST_STATE_READY_TO_NULL: - /* We are cleaning our resources here, yes i know chain is not running - but the interface can be called to set a window from a different thread - and that would crash */ - g_mutex_lock (xvimagesink->stream_lock); if (xvimagesink->xvimage) { gst_xvimagesink_xvimage_destroy (xvimagesink, xvimagesink->xvimage); xvimagesink->xvimage = NULL; @@ -1392,45 +1380,38 @@ gst_xvimagesink_change_state (GstElement * element) gst_xvimagesink_xcontext_clear (xvimagesink); xvimagesink->xcontext = NULL; } - g_mutex_unlock (xvimagesink->stream_lock); break; } - if (GST_ELEMENT_CLASS (parent_class)->change_state) - return GST_ELEMENT_CLASS (parent_class)->change_state (element); - - return GST_STATE_SUCCESS; + return GST_ELEMENT_CLASS (parent_class)->change_state (element); } static void -gst_xvimagesink_chain (GstPad * pad, GstData * data) +gst_xvimagesink_get_times (GstBaseSink * bsink, GstBuffer * buf, + GstClockTime * start, GstClockTime * end) { - GstBuffer *buf = NULL; GstXvImageSink *xvimagesink; - g_return_if_fail (GST_IS_PAD (pad)); - g_return_if_fail (data != NULL); + xvimagesink = GST_XVIMAGESINK (bsink); - xvimagesink = GST_XVIMAGESINK (gst_pad_get_parent (pad)); - - if (GST_IS_EVENT (data)) { - gst_pad_event_default (pad, GST_EVENT (data)); - return; - } - - g_mutex_lock (xvimagesink->stream_lock); - - buf = GST_BUFFER (data); - /* update time */ if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) { - xvimagesink->time = GST_BUFFER_TIMESTAMP (buf); + *start = GST_BUFFER_TIMESTAMP (buf); + if (GST_BUFFER_DURATION_IS_VALID (buf)) { + *end = *start + GST_BUFFER_DURATION (buf); + } else { + if (xvimagesink->framerate > 0) { + *end = *start + GST_SECOND / xvimagesink->framerate; + } + } } - GST_LOG_OBJECT (xvimagesink, "clock wait: %" GST_TIME_FORMAT, - GST_TIME_ARGS (xvimagesink->time)); +} - if (GST_VIDEOSINK_CLOCK (xvimagesink)) { - gst_element_wait (GST_ELEMENT (xvimagesink), xvimagesink->time); - } +static GstFlowReturn +gst_xvimagesink_show_frame (GstBaseSink * bsink, GstBuffer * buf) +{ + GstXvImageSink *xvimagesink; + + xvimagesink = GST_XVIMAGESINK (bsink); /* If this buffer has been allocated using our buffer management we simply put the ximage which is in the PRIVATE pointer */ @@ -1448,8 +1429,7 @@ gst_xvimagesink_chain (GstPad * pad, GstData * data) gst_buffer_unref (buf); GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL), ("Failed creating an XvImage in xvimagesink chain function.")); - g_mutex_unlock (xvimagesink->stream_lock); - return; + return GST_FLOW_ERROR; } } @@ -1459,18 +1439,49 @@ gst_xvimagesink_chain (GstPad * pad, GstData * data) gst_xvimagesink_xvimage_put (xvimagesink, xvimagesink->xvimage); } - /* set correct time for next buffer */ - if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf) && xvimagesink->framerate > 0) { - xvimagesink->time += GST_SECOND / xvimagesink->framerate; + gst_xvimagesink_handle_xevents (xvimagesink, GST_VIDEOSINK_PAD (xvimagesink)); + + return GST_FLOW_OK; +} + +#if 0 +static GstFlowReturn +gst_xvimagesink_chain (GstPad * pad, GstBuffer * buffer) +{ + GstBuffer *buf = NULL; + GstXvImageSink *xvimagesink; + GstCaps *caps; + GstFlowReturn result; + + xvimagesink = GST_XVIMAGESINK (GST_PAD_PARENT (pad)); + + buf = GST_BUFFER (buffer); + + caps = gst_buffer_get_caps (buffer); + if (caps && caps != GST_PAD_CAPS (pad)) { + if (gst_xvimagesink_parse_caps (pad, caps)) { + gst_pad_set_caps (pad, caps); + } else { + GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL), + ("received unkown format")); + gst_element_abort_preroll (GST_ELEMENT (xvimagesink)); + gst_buffer_unref (buffer); + return GST_FLOW_NOT_NEGOTIATED; + } + } + if (!GST_PAD_CAPS (pad)) { + gst_buffer_unref (buffer); + return GST_FLOW_NOT_NEGOTIATED; } - gst_buffer_unref (buf); + gst_xvimagesink_show_frame (xvimagesink, buf); + gst_xvimagesink_handle_xevents (xvimagesink, pad); - gst_xvimagesink_send_pending_navigation (xvimagesink); - g_mutex_unlock (xvimagesink->stream_lock); + return result; } +#endif /* Buffer management */ @@ -1486,8 +1497,8 @@ gst_xvimagesink_buffer_free (GstBuffer * buffer) xvimagesink = xvimage->xvimagesink; /* If our geometry changed we can't reuse that image. */ - if ((xvimage->width != xvimagesink->video_width) || - (xvimage->height != xvimagesink->video_height)) + if ((xvimage->width != GST_VIDEOSINK_WIDTH (xvimagesink)) || + (xvimage->height != GST_VIDEOSINK_HEIGHT (xvimagesink))) gst_xvimagesink_xvimage_destroy (xvimagesink, xvimage); else { /* In that case we can reuse the image and add it to our image pool. */ @@ -1499,20 +1510,28 @@ gst_xvimagesink_buffer_free (GstBuffer * buffer) } static GstBuffer * -gst_xvimagesink_buffer_alloc (GstPad * pad, guint64 offset, guint size) +gst_xvimagesink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size, + GstCaps * caps) { GstXvImageSink *xvimagesink; GstBuffer *buffer; GstXvImage *xvimage = NULL; - gboolean not_found = TRUE; - xvimagesink = GST_XVIMAGESINK (gst_pad_get_parent (pad)); + xvimagesink = GST_XVIMAGESINK (bsink); + + /* FIXME, we should just parse the caps, and provide a buffer in this format, + * we should not just reconfigure ourselves yet */ + if (caps && caps != GST_PAD_CAPS (GST_VIDEOSINK_PAD (xvimagesink))) { + if (!gst_xvimagesink_setcaps (bsink, caps)) { + return NULL; + } + } g_mutex_lock (xvimagesink->pool_lock); /* Walking through the pool cleaning unusable images and searching for a suitable one */ - while (not_found && xvimagesink->image_pool) { + while (xvimagesink->image_pool) { xvimage = xvimagesink->image_pool->data; if (xvimage) { @@ -1521,8 +1540,8 @@ gst_xvimagesink_buffer_alloc (GstPad * pad, guint64 offset, guint size) xvimagesink->image_pool); /* We check for geometry or image format changes */ - if ((xvimage->width != xvimagesink->video_width) || - (xvimage->height != xvimagesink->video_height) || + if ((xvimage->width != GST_VIDEOSINK_WIDTH (xvimagesink)) || + (xvimage->height != GST_VIDEOSINK_HEIGHT (xvimagesink)) || (xvimage->im_format != xvimagesink->xcontext->im_format)) { /* This image is unusable. Destroying... */ gst_xvimagesink_xvimage_destroy (xvimagesink, xvimage); @@ -1538,7 +1557,7 @@ gst_xvimagesink_buffer_alloc (GstPad * pad, guint64 offset, guint size) if (!xvimage) { /* We found no suitable image in the pool. Creating... */ - GST_LOG_OBJECT (xvimagesink, "no usable image in pool, creating xvimage"); + GST_DEBUG_OBJECT (xvimagesink, "no usable image in pool, creating xvimage"); xvimage = gst_xvimagesink_xvimage_new (xvimagesink, xvimagesink->video_width, xvimagesink->video_height); } @@ -1552,6 +1571,7 @@ gst_xvimagesink_buffer_alloc (GstPad * pad, guint64 offset, guint size) GST_BUFFER_DATA (buffer) = xvimage->xvimage->data; GST_BUFFER_FREE_DATA_FUNC (buffer) = gst_xvimagesink_buffer_free; GST_BUFFER_SIZE (buffer) = xvimage->size; + gst_buffer_set_caps (buffer, caps); return buffer; } else return NULL; @@ -1573,6 +1593,7 @@ gst_xvimagesink_interface_init (GstImplementsInterfaceClass * klass) klass->supported = gst_xvimagesink_interface_supported; } +#if 0 /* * This function is called with the stream-lock held */ @@ -1622,6 +1643,7 @@ gst_xvimagesink_send_pending_navigation (GstXvImageSink * xvimagesink) g_slist_free (pend_events); } +#endif static void gst_xvimagesink_navigation_send_event (GstNavigation * navigation, @@ -1629,14 +1651,25 @@ gst_xvimagesink_navigation_send_event (GstNavigation * navigation, { GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation); GstEvent *event; + double x, y; event = gst_event_new (GST_EVENT_NAVIGATION); event->event_data.structure.structure = structure; - g_mutex_lock (xvimagesink->nav_lock); - xvimagesink->pend_nav_events = - g_slist_prepend (xvimagesink->pend_nav_events, event); - g_mutex_unlock (xvimagesink->nav_lock); + /* Converting pointer coordinates to the non scaled geometry */ + if (gst_structure_get_double (structure, "pointer_x", &x)) { + x *= GST_VIDEOSINK_WIDTH (xvimagesink); + x /= xvimagesink->xwindow->width; + gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL); + } + if (gst_structure_get_double (structure, "pointer_y", &y)) { + y *= GST_VIDEOSINK_HEIGHT (xvimagesink); + y /= xvimagesink->xwindow->height; + gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL); + } + + gst_pad_send_event (gst_pad_get_peer (GST_VIDEOSINK_PAD (xvimagesink)), + event); } static void @@ -1666,11 +1699,6 @@ gst_xvimagesink_set_xwindow_id (GstXOverlay * overlay, XID xwindow_id) gst_xvimagesink_update_colorbalance (xvimagesink); - /* We acquire the stream lock while setting this window in the element. - We are basically cleaning tons of stuff replacing the old window, putting - images while we do that would surely crash */ - g_mutex_lock (xvimagesink->stream_lock); - /* Clear image pool as the images are unusable anyway */ gst_xvimagesink_imagepool_clear (xvimagesink); @@ -1718,8 +1746,6 @@ gst_xvimagesink_set_xwindow_id (GstXOverlay * overlay, XID xwindow_id) if (xwindow) xvimagesink->xwindow = xwindow; - - g_mutex_unlock (xvimagesink->stream_lock); } static void @@ -1741,10 +1767,6 @@ gst_xvimagesink_expose (GstXOverlay * overlay) if (!xvimagesink->xwindow) return; - /* We don't want chain to iterate while we do that. We might act on random - cur_image and different geometry */ - g_mutex_lock (xvimagesink->stream_lock); - /* Update the window geometry */ g_mutex_lock (xvimagesink->x_lock); XGetWindowAttributes (xvimagesink->xcontext->disp, @@ -1757,8 +1779,6 @@ gst_xvimagesink_expose (GstXOverlay * overlay) if (xvimagesink->cur_image) { gst_xvimagesink_xvimage_put (xvimagesink, xvimagesink->cur_image); } - - g_mutex_unlock (xvimagesink->stream_lock); } static void @@ -1894,11 +1914,9 @@ gst_xvimagesink_set_property (GObject * object, guint prop_id, case ARG_SYNCHRONOUS: xvimagesink->synchronous = g_value_get_boolean (value); if (xvimagesink->xcontext) { - g_mutex_lock (xvimagesink->x_lock); XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous); GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s", xvimagesink->synchronous ? "TRUE" : "FALSE"); - g_mutex_unlock (xvimagesink->x_lock); } break; case ARG_PIXEL_ASPECT_RATIO: @@ -1981,27 +1999,10 @@ gst_xvimagesink_finalize (GObject * object) g_mutex_free (xvimagesink->x_lock); xvimagesink->x_lock = NULL; } - if (xvimagesink->stream_lock) { - g_mutex_free (xvimagesink->stream_lock); - xvimagesink->stream_lock = NULL; - } if (xvimagesink->pool_lock) { g_mutex_free (xvimagesink->pool_lock); xvimagesink->pool_lock = NULL; } - if (xvimagesink->nav_lock) { - g_mutex_free (xvimagesink->nav_lock); - xvimagesink->nav_lock = NULL; - } - - while (xvimagesink->pend_nav_events) { - GstEvent *event = xvimagesink->pend_nav_events->data; - - xvimagesink->pend_nav_events = - g_slist_delete_link (xvimagesink->pend_nav_events, - xvimagesink->pend_nav_events); - gst_event_unref (event); - } G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -2009,24 +2010,6 @@ gst_xvimagesink_finalize (GObject * object) static void gst_xvimagesink_init (GstXvImageSink * xvimagesink) { - GST_VIDEOSINK_PAD (xvimagesink) = - gst_pad_new_from_template (gst_static_pad_template_get - (&gst_xvimagesink_sink_template_factory), "sink"); - - gst_element_add_pad (GST_ELEMENT (xvimagesink), - GST_VIDEOSINK_PAD (xvimagesink)); - - gst_pad_set_chain_function (GST_VIDEOSINK_PAD (xvimagesink), - gst_xvimagesink_chain); - gst_pad_set_link_function (GST_VIDEOSINK_PAD (xvimagesink), - gst_xvimagesink_sink_link); - gst_pad_set_getcaps_function (GST_VIDEOSINK_PAD (xvimagesink), - gst_xvimagesink_getcaps); - gst_pad_set_fixate_function (GST_VIDEOSINK_PAD (xvimagesink), - gst_xvimagesink_fixate); - gst_pad_set_bufferalloc_function (GST_VIDEOSINK_PAD (xvimagesink), - gst_xvimagesink_buffer_alloc); - xvimagesink->display_name = NULL; xvimagesink->xcontext = NULL; xvimagesink->xwindow = NULL; @@ -2042,19 +2025,12 @@ gst_xvimagesink_init (GstXvImageSink * xvimagesink) xvimagesink->video_height = 0; xvimagesink->x_lock = g_mutex_new (); - xvimagesink->stream_lock = g_mutex_new (); xvimagesink->image_pool = NULL; xvimagesink->pool_lock = g_mutex_new (); xvimagesink->synchronous = FALSE; xvimagesink->par = NULL; - - GST_FLAG_SET (xvimagesink, GST_ELEMENT_THREAD_SUGGESTED); - GST_FLAG_SET (xvimagesink, GST_ELEMENT_EVENT_AWARE); - - xvimagesink->nav_lock = g_mutex_new (); - xvimagesink->pend_nav_events = NULL; } static void @@ -2073,12 +2049,17 @@ gst_xvimagesink_class_init (GstXvImageSinkClass * klass) { GObjectClass *gobject_class; GstElementClass *gstelement_class; + GstBaseSinkClass *gstbasesink_class; gobject_class = (GObjectClass *) klass; gstelement_class = (GstElementClass *) klass; + gstbasesink_class = (GstBaseSinkClass *) klass; parent_class = g_type_class_ref (GST_TYPE_VIDEOSINK); + gobject_class->set_property = gst_xvimagesink_set_property; + gobject_class->get_property = gst_xvimagesink_get_property; + g_object_class_install_property (gobject_class, ARG_CONTRAST, g_param_spec_int ("contrast", "Contrast", "The contrast of the video", -1000, 1000, 0, G_PARAM_READWRITE)); @@ -2104,10 +2085,18 @@ gst_xvimagesink_class_init (GstXvImageSinkClass * klass) "The pixel aspect ratio of the device", "1/1", G_PARAM_READWRITE)); gobject_class->finalize = gst_xvimagesink_finalize; - gobject_class->set_property = gst_xvimagesink_set_property; - gobject_class->get_property = gst_xvimagesink_get_property; - gstelement_class->change_state = gst_xvimagesink_change_state; + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_xvimagesink_change_state); + + //gstbasesink_class->get_template = GST_DEBUG_FUNCPTR (gst_xvimagesink_get_template); + gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_getcaps); + gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_setcaps); + gstbasesink_class->buffer_alloc = + GST_DEBUG_FUNCPTR (gst_xvimagesink_buffer_alloc); + gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_xvimagesink_get_times); + gstbasesink_class->preroll = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame); + gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame); } /* ============================================================= */ diff --git a/sys/xvimage/xvimagesink.h b/sys/xvimage/xvimagesink.h index 56025a2884..2d3ba7c158 100644 --- a/sys/xvimage/xvimagesink.h +++ b/sys/xvimage/xvimagesink.h @@ -142,21 +142,15 @@ struct _GstXvImageSink { gboolean cb_changed; GMutex *x_lock; - GMutex *stream_lock; guint video_width, video_height; /* size of incoming video; * used as the size for XvImage */ GValue *par; /* object-set pixel aspect ratio */ - GstClockTime time; - GMutex *pool_lock; GSList *image_pool; gboolean synchronous; - - GMutex *nav_lock; - GSList *pend_nav_events; }; struct _GstXvImageSinkClass { diff --git a/tests/examples/seek/Makefile.am b/tests/examples/seek/Makefile.am index 582306ac8a..3e7c7886db 100644 --- a/tests/examples/seek/Makefile.am +++ b/tests/examples/seek/Makefile.am @@ -1,4 +1,4 @@ -examples = seek spider_seek cdplayer cdparanoia vorbisfile playbin chained +examples = seek cdplayer cdparanoia noinst_PROGRAMS = $(examples) diff --git a/tests/examples/seek/playbin.c b/tests/examples/seek/playbin.c deleted file mode 100644 index b6ff35b8e2..0000000000 --- a/tests/examples/seek/playbin.c +++ /dev/null @@ -1,270 +0,0 @@ -#include -#include -#include -#include -#include - -static GstElement *playbin = NULL; -static GstElement *pipeline; -static guint64 duration; -static GtkAdjustment *adjustment; -static GtkWidget *hscale; -static gboolean verbose = FALSE; - -static guint update_id; - -#define UPDATE_INTERVAL 500 - -static GstElement * -make_playerbin_pipeline (const gchar * location) -{ - playbin = gst_element_factory_make ("playbin", "player"); - g_assert (playbin); - - g_object_set (G_OBJECT (playbin), "uri", location, NULL); - - return playbin; -} - -static gchar * -format_value (GtkScale * scale, gdouble value) -{ - gint64 real; - gint64 seconds; - gint64 subseconds; - - real = value * duration / 100; - seconds = (gint64) real / GST_SECOND; - subseconds = (gint64) real / (GST_SECOND / 100); - - return g_strdup_printf ("%02" G_GINT64_FORMAT ":%02" G_GINT64_FORMAT ":%02" - G_GINT64_FORMAT, seconds / 60, seconds % 60, subseconds % 100); -} - -static gboolean -update_scale (gpointer data) -{ - GstClock *clock; - guint64 position; - GstFormat format = GST_FORMAT_TIME; - gboolean res; - - duration = 0; - clock = gst_bin_get_clock (GST_BIN (pipeline)); - - res = gst_element_query (playbin, GST_QUERY_TOTAL, &format, &duration); - if (!res) - duration = 0; - res = gst_element_query (playbin, GST_QUERY_POSITION, &format, &position); - if (!res) - position = 0; - - if (position >= duration) - duration = position; - - if (duration > 0) { - gtk_adjustment_set_value (adjustment, position * 100.0 / duration); - gtk_widget_queue_draw (hscale); - } - - return TRUE; -} - -static gboolean -iterate (gpointer data) -{ - gboolean res; - - if (!GST_FLAG_IS_SET (GST_OBJECT (data), GST_BIN_SELF_SCHEDULABLE)) { - res = gst_bin_iterate (GST_BIN (data)); - } else { - g_usleep (UPDATE_INTERVAL); - res = gst_element_get_state (GST_ELEMENT (data)) == GST_STATE_PLAYING; - } - - if (!res) { - gtk_timeout_remove (update_id); - g_print ("stopping iterations\n"); - } - return res; -} - -static gboolean -start_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data) -{ - gst_element_set_state (pipeline, GST_STATE_PAUSED); - gtk_timeout_remove (update_id); - - return FALSE; -} - -static gboolean -stop_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data) -{ - gint64 real = gtk_range_get_value (GTK_RANGE (widget)) * duration / 100; - gboolean res; - GstEvent *s_event; - - g_print ("seek to %" G_GINT64_FORMAT " on element %s\n", real, - gst_element_get_name (playbin)); - s_event = - gst_event_new_seek (GST_FORMAT_TIME | GST_SEEK_METHOD_SET | - GST_SEEK_FLAG_FLUSH, real); - - res = gst_element_send_event (playbin, s_event); - - gst_element_set_state (pipeline, GST_STATE_PLAYING); - gtk_idle_add ((GtkFunction) iterate, pipeline); - update_id = - gtk_timeout_add (UPDATE_INTERVAL, (GtkFunction) update_scale, pipeline); - - return FALSE; -} - -static void -print_media_info (GstElement * playbin) -{ - GList *streaminfo; - GList *s; - - g_print ("have media info now\n"); - - /* get info about the stream */ - g_object_get (G_OBJECT (playbin), "stream-info", &streaminfo, NULL); - - for (s = streaminfo; s; s = g_list_next (s)) { - GObject *obj = G_OBJECT (s->data); - gint type; - gboolean mute; - - g_object_get (obj, "type", &type, NULL); - g_object_get (obj, "mute", &mute, NULL); - - g_print ("%d %d\n", type, mute); - } -} - -static void -play_cb (GtkButton * button, gpointer data) -{ - if (gst_element_get_state (pipeline) != GST_STATE_PLAYING) { - GstElementStateReturn res; - - res = gst_element_set_state (pipeline, GST_STATE_PAUSED); - if (res == GST_STATE_SUCCESS) { - print_media_info (playbin); - - res = gst_element_set_state (pipeline, GST_STATE_PLAYING); - gtk_idle_add ((GtkFunction) iterate, pipeline); - update_id = - gtk_timeout_add (UPDATE_INTERVAL, (GtkFunction) update_scale, - pipeline); - } else { - g_print ("failed playing\n"); - } - } -} - -static void -pause_cb (GtkButton * button, gpointer data) -{ - if (gst_element_get_state (pipeline) != GST_STATE_PAUSED) { - gst_element_set_state (pipeline, GST_STATE_PAUSED); - gtk_timeout_remove (update_id); - } -} - -static void -stop_cb (GtkButton * button, gpointer data) -{ - if (gst_element_get_state (pipeline) != GST_STATE_READY) { - gst_element_set_state (pipeline, GST_STATE_READY); - gtk_adjustment_set_value (adjustment, 0.0); - gtk_timeout_remove (update_id); - } -} - -static void -print_usage (int argc, char **argv) -{ - g_print ("usage: %s \n", argv[0]); -} - -int -main (int argc, char **argv) -{ - GtkWidget *window, *hbox, *vbox, *play_button, *pause_button, *stop_button; - struct poptOption options[] = { - {"verbose", 'v', POPT_ARG_NONE | POPT_ARGFLAG_STRIP, &verbose, 0, - "Verbose properties", NULL}, - POPT_TABLEEND - }; - - gst_init_with_popt_table (&argc, &argv, options); - gtk_init (&argc, &argv); - - if (argc != 2) { - print_usage (argc, argv); - exit (-1); - } - - pipeline = make_playerbin_pipeline (argv[1]); - g_assert (pipeline); - - /* initialize gui elements ... */ - window = gtk_window_new (GTK_WINDOW_TOPLEVEL); - hbox = gtk_hbox_new (FALSE, 0); - vbox = gtk_vbox_new (FALSE, 0); - play_button = gtk_button_new_with_label ("play"); - pause_button = gtk_button_new_with_label ("pause"); - stop_button = gtk_button_new_with_label ("stop"); - - adjustment = - GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.00, 100.0, 0.1, 1.0, 1.0)); - hscale = gtk_hscale_new (adjustment); - gtk_scale_set_digits (GTK_SCALE (hscale), 2); - gtk_range_set_update_policy (GTK_RANGE (hscale), GTK_UPDATE_CONTINUOUS); - - gtk_signal_connect (GTK_OBJECT (hscale), - "button_press_event", G_CALLBACK (start_seek), pipeline); - gtk_signal_connect (GTK_OBJECT (hscale), - "button_release_event", G_CALLBACK (stop_seek), pipeline); - gtk_signal_connect (GTK_OBJECT (hscale), - "format_value", G_CALLBACK (format_value), pipeline); - - /* do the packing stuff ... */ - gtk_window_set_default_size (GTK_WINDOW (window), 96, 96); - gtk_container_add (GTK_CONTAINER (window), vbox); - gtk_container_add (GTK_CONTAINER (vbox), hbox); - gtk_box_pack_start (GTK_BOX (hbox), play_button, FALSE, FALSE, 2); - gtk_box_pack_start (GTK_BOX (hbox), pause_button, FALSE, FALSE, 2); - gtk_box_pack_start (GTK_BOX (hbox), stop_button, FALSE, FALSE, 2); - gtk_box_pack_start (GTK_BOX (vbox), hscale, TRUE, TRUE, 2); - - /* connect things ... */ - g_signal_connect (G_OBJECT (play_button), "clicked", G_CALLBACK (play_cb), - pipeline); - g_signal_connect (G_OBJECT (pause_button), "clicked", G_CALLBACK (pause_cb), - pipeline); - g_signal_connect (G_OBJECT (stop_button), "clicked", G_CALLBACK (stop_cb), - pipeline); - g_signal_connect (G_OBJECT (window), "delete_event", gtk_main_quit, NULL); - - /* show the gui. */ - gtk_widget_show_all (window); - - if (verbose) { - g_signal_connect (pipeline, "deep_notify", - G_CALLBACK (gst_element_default_deep_notify), NULL); - } - g_signal_connect (pipeline, "error", G_CALLBACK (gst_element_default_error), - NULL); - - gtk_main (); - - gst_element_set_state (pipeline, GST_STATE_NULL); - - gst_object_unref (GST_OBJECT (pipeline)); - - return 0; -} diff --git a/tests/examples/seek/seek.c b/tests/examples/seek/seek.c index fdd7c45d18..ef73db6b88 100644 --- a/tests/examples/seek/seek.c +++ b/tests/examples/seek/seek.c @@ -1,6 +1,3 @@ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif #include #include #include @@ -20,12 +17,18 @@ static gboolean elem_seek = FALSE; static gboolean verbose = FALSE; static guint update_id; +static guint seek_timeout_id = 0; +static gulong changed_id; //#define SOURCE "gnomevfssrc" -#define SOURCE "filesrc" +#define SOURCE "gnomevfssrc" #define UPDATE_INTERVAL 500 +/* number of milliseconds to play for after a seek */ +#define SCRUB_TIME 250 +#define SCRUB + #define THREAD #define PAD_SEEK @@ -54,13 +57,13 @@ dynamic_link (GstPadTemplate * templ, GstPad * newpad, gpointer data) { dyn_link *connect = (dyn_link *) data; - if (!strcmp (gst_pad_get_name (newpad), connect->padname)) { - gst_element_set_state (pipeline, GST_STATE_PAUSED); - gst_bin_add (GST_BIN (pipeline), connect->bin); + if (connect->padname == NULL || + !strcmp (gst_pad_get_name (newpad), connect->padname)) { + if (connect->bin) + gst_bin_add (GST_BIN (pipeline), connect->bin); gst_pad_link (newpad, connect->target); - gst_element_set_state (pipeline, GST_STATE_PLAYING); - seekable_pads = g_list_prepend (seekable_pads, newpad); + //seekable_pads = g_list_prepend (seekable_pads, newpad); rate_pads = g_list_prepend (rate_pads, newpad); } } @@ -91,7 +94,7 @@ make_mod_pipeline (const gchar * location) src = gst_element_factory_make_or_warn (SOURCE, "src"); decoder = gst_element_factory_make_or_warn ("modplug", "decoder"); - audiosink = gst_element_factory_make_or_warn (DEFAULT_AUDIOSINK, "sink"); + audiosink = gst_element_factory_make_or_warn ("osssink", "sink"); //g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL); g_object_set (G_OBJECT (src), "location", location, NULL); @@ -122,8 +125,8 @@ make_dv_pipeline (const gchar * location) src = gst_element_factory_make_or_warn (SOURCE, "src"); decoder = gst_element_factory_make_or_warn ("dvdec", "decoder"); - videosink = gst_element_factory_make_or_warn (DEFAULT_VIDEOSINK, "v_sink"); - audiosink = gst_element_factory_make_or_warn (DEFAULT_AUDIOSINK, "a_sink"); + videosink = gst_element_factory_make_or_warn ("ximagesink", "v_sink"); + audiosink = gst_element_factory_make_or_warn ("osssink", "a_sink"); //g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL); g_object_set (G_OBJECT (src), "location", location, NULL); @@ -159,7 +162,7 @@ make_wav_pipeline (const gchar * location) src = gst_element_factory_make_or_warn (SOURCE, "src"); decoder = gst_element_factory_make_or_warn ("wavparse", "decoder"); - audiosink = gst_element_factory_make_or_warn (DEFAULT_AUDIOSINK, "sink"); + audiosink = gst_element_factory_make_or_warn ("osssink", "sink"); //g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL); g_object_set (G_OBJECT (src), "location", location, NULL); @@ -190,7 +193,7 @@ make_flac_pipeline (const gchar * location) src = gst_element_factory_make_or_warn (SOURCE, "src"); decoder = gst_element_factory_make_or_warn ("flacdec", "decoder"); - audiosink = gst_element_factory_make_or_warn (DEFAULT_AUDIOSINK, "sink"); + audiosink = gst_element_factory_make_or_warn ("osssink", "sink"); g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL); g_object_set (G_OBJECT (src), "location", location, NULL); @@ -221,7 +224,7 @@ make_sid_pipeline (const gchar * location) src = gst_element_factory_make_or_warn (SOURCE, "src"); decoder = gst_element_factory_make_or_warn ("siddec", "decoder"); - audiosink = gst_element_factory_make_or_warn (DEFAULT_AUDIOSINK, "sink"); + audiosink = gst_element_factory_make_or_warn ("osssink", "sink"); //g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL); g_object_set (G_OBJECT (src), "location", location, NULL); @@ -276,25 +279,35 @@ make_parse_pipeline (const gchar * location) static GstElement * make_vorbis_pipeline (const gchar * location) { - GstElement *pipeline; - GstElement *src, *decoder, *audiosink; + GstElement *pipeline, *audio_bin; + GstElement *src, *demux, *decoder, *convert, *audiosink; GstPad *seekable; pipeline = gst_pipeline_new ("app"); src = gst_element_factory_make_or_warn (SOURCE, "src"); - decoder = gst_element_factory_make_or_warn ("vorbisfile", "decoder"); - audiosink = gst_element_factory_make_or_warn (DEFAULT_AUDIOSINK, "sink"); + demux = gst_element_factory_make_or_warn ("oggdemux", "demux"); + decoder = gst_element_factory_make_or_warn ("vorbisdec", "decoder"); + convert = gst_element_factory_make_or_warn ("audioconvert", "convert"); + audiosink = gst_element_factory_make_or_warn ("osssink", "sink"); g_object_set (G_OBJECT (audiosink), "sync", TRUE, NULL); g_object_set (G_OBJECT (src), "location", location, NULL); - gst_bin_add (GST_BIN (pipeline), src); - gst_bin_add (GST_BIN (pipeline), decoder); - gst_bin_add (GST_BIN (pipeline), audiosink); + audio_bin = gst_bin_new ("a_decoder_bin"); - gst_element_link (src, decoder); - gst_element_link (decoder, audiosink); + gst_bin_add (GST_BIN (pipeline), src); + gst_bin_add (GST_BIN (pipeline), demux); + gst_bin_add (GST_BIN (audio_bin), decoder); + gst_bin_add (GST_BIN (audio_bin), convert); + gst_bin_add (GST_BIN (audio_bin), audiosink); + gst_bin_add (GST_BIN (pipeline), audio_bin); + + gst_element_link (src, demux); + gst_element_link (decoder, convert); + gst_element_link (convert, audiosink); + + setup_dynamic_link (demux, NULL, gst_element_get_pad (decoder, "sink"), NULL); seekable = gst_element_get_pad (decoder, "src"); seekable_pads = g_list_prepend (seekable_pads, seekable); @@ -304,11 +317,185 @@ make_vorbis_pipeline (const gchar * location) return pipeline; } +static GstElement * +make_theora_pipeline (const gchar * location) +{ + GstElement *pipeline, *video_bin; + GstElement *src, *demux, *decoder, *convert, *videosink; + GstElement *queue; + GstPad *seekable; + + pipeline = gst_pipeline_new ("app"); + + src = gst_element_factory_make_or_warn (SOURCE, "src"); + demux = gst_element_factory_make_or_warn ("oggdemux", "demux"); + queue = gst_element_factory_make_or_warn ("queue", "queue"); + decoder = gst_element_factory_make_or_warn ("theoradec", "decoder"); + convert = gst_element_factory_make_or_warn ("ffmpegcolorspace", "convert"); + videosink = gst_element_factory_make_or_warn ("xvimagesink", "sink"); + + g_object_set (G_OBJECT (src), "location", location, NULL); + + video_bin = gst_bin_new ("v_decoder_bin"); + + gst_bin_add (GST_BIN (pipeline), src); + gst_bin_add (GST_BIN (pipeline), demux); + gst_bin_add (GST_BIN (pipeline), queue); + gst_bin_add (GST_BIN (video_bin), decoder); + gst_bin_add (GST_BIN (video_bin), convert); + gst_bin_add (GST_BIN (video_bin), videosink); + gst_bin_add (GST_BIN (pipeline), video_bin); + + gst_element_link (src, demux); + gst_element_link (queue, decoder); + gst_element_link (decoder, convert); + gst_element_link (convert, videosink); + + setup_dynamic_link (demux, NULL, gst_element_get_pad (queue, "sink"), NULL); + + seekable = gst_element_get_pad (decoder, "src"); + seekable_pads = g_list_prepend (seekable_pads, seekable); + rate_pads = g_list_prepend (rate_pads, seekable); + rate_pads = g_list_prepend (rate_pads, gst_element_get_pad (decoder, "sink")); + + return pipeline; +} + +static GstElement * +make_vorbis_theora_pipeline (const gchar * location) +{ + GstElement *pipeline, *audio_bin, *video_bin; + GstElement *src, *demux, *a_decoder, *a_convert, *v_decoder, *v_convert; + GstElement *audiosink, *videosink; + GstElement *a_queue, *v_queue; + GstPad *seekable; + + pipeline = gst_pipeline_new ("app"); + + src = gst_element_factory_make_or_warn (SOURCE, "src"); + g_object_set (G_OBJECT (src), "location", location, NULL); + + demux = gst_element_factory_make_or_warn ("oggdemux", "demux"); + + gst_bin_add (GST_BIN (pipeline), src); + gst_bin_add (GST_BIN (pipeline), demux); + gst_element_link (src, demux); + + audio_bin = gst_bin_new ("a_decoder_bin"); + a_queue = gst_element_factory_make_or_warn ("queue", "a_queue"); + a_decoder = gst_element_factory_make_or_warn ("vorbisdec", "a_dec"); + a_convert = gst_element_factory_make_or_warn ("audioconvert", "a_convert"); + audiosink = gst_element_factory_make_or_warn ("osssink", "a_sink"); + + gst_element_link (a_queue, a_decoder); + gst_element_link (a_decoder, a_convert); + gst_element_link (a_convert, audiosink); + + gst_bin_add (GST_BIN (audio_bin), a_queue); + gst_bin_add (GST_BIN (audio_bin), a_decoder); + gst_bin_add (GST_BIN (audio_bin), a_convert); + gst_bin_add (GST_BIN (audio_bin), audiosink); + + gst_bin_add (GST_BIN (pipeline), audio_bin); + + setup_dynamic_link (demux, NULL, gst_element_get_pad (a_queue, "sink"), NULL); + + video_bin = gst_bin_new ("v_decoder_bin"); + v_queue = gst_element_factory_make_or_warn ("queue", "v_queue"); + v_decoder = gst_element_factory_make_or_warn ("theoradec", "v_dec"); + v_convert = + gst_element_factory_make_or_warn ("ffmpegcolorspace", "v_convert"); + videosink = gst_element_factory_make_or_warn ("xvimagesink", "v_sink"); + gst_element_link_many (v_queue, v_decoder, v_convert, videosink, NULL); + + gst_bin_add (GST_BIN (video_bin), v_queue); + gst_bin_add (GST_BIN (video_bin), v_decoder); + gst_bin_add (GST_BIN (video_bin), v_convert); + gst_bin_add (GST_BIN (video_bin), videosink); + + gst_bin_add (GST_BIN (pipeline), video_bin); + + setup_dynamic_link (demux, NULL, gst_element_get_pad (v_queue, "sink"), NULL); + + seekable = gst_element_get_pad (a_decoder, "src"); + seekable_pads = g_list_prepend (seekable_pads, seekable); + rate_pads = g_list_prepend (rate_pads, seekable); + rate_pads = + g_list_prepend (rate_pads, gst_element_get_pad (a_decoder, "sink")); + + return pipeline; +} + +static GstElement * +make_avi_msmpeg4v3_mp3_pipeline (const gchar * location) +{ + GstElement *pipeline, *audio_bin, *video_bin; + GstElement *src, *demux, *a_decoder, *a_convert, *v_decoder, *v_convert; + GstElement *audiosink, *videosink; + GstElement *a_queue, *v_queue; + GstPad *seekable; + + pipeline = gst_pipeline_new ("app"); + + src = gst_element_factory_make_or_warn (SOURCE, "src"); + g_object_set (G_OBJECT (src), "location", location, NULL); + + demux = gst_element_factory_make_or_warn ("avidemux", "demux"); + + gst_bin_add (GST_BIN (pipeline), src); + gst_bin_add (GST_BIN (pipeline), demux); + gst_element_link (src, demux); + + audio_bin = gst_bin_new ("a_decoder_bin"); + a_queue = gst_element_factory_make_or_warn ("queue", "a_queue"); + a_decoder = gst_element_factory_make_or_warn ("mad", "a_dec"); + a_convert = gst_element_factory_make_or_warn ("audioconvert", "a_convert"); + audiosink = gst_element_factory_make_or_warn ("osssink", "a_sink"); + + gst_element_link (a_queue, a_decoder); + gst_element_link (a_decoder, a_convert); + gst_element_link (a_convert, audiosink); + + gst_bin_add (GST_BIN (audio_bin), a_queue); + gst_bin_add (GST_BIN (audio_bin), a_decoder); + gst_bin_add (GST_BIN (audio_bin), a_convert); + gst_bin_add (GST_BIN (audio_bin), audiosink); + + gst_bin_add (GST_BIN (pipeline), audio_bin); + + setup_dynamic_link (demux, NULL, gst_element_get_pad (a_queue, "sink"), NULL); + + video_bin = gst_bin_new ("v_decoder_bin"); + v_queue = gst_element_factory_make_or_warn ("queue", "v_queue"); + v_decoder = gst_element_factory_make_or_warn ("ffdec_msmpeg4", "v_dec"); + v_convert = + gst_element_factory_make_or_warn ("ffmpegcolorspace", "v_convert"); + videosink = gst_element_factory_make_or_warn ("xvimagesink", "v_sink"); + gst_element_link_many (v_queue, v_decoder, v_convert, videosink, NULL); + + gst_bin_add (GST_BIN (video_bin), v_queue); + gst_bin_add (GST_BIN (video_bin), v_decoder); + gst_bin_add (GST_BIN (video_bin), v_convert); + gst_bin_add (GST_BIN (video_bin), videosink); + + gst_bin_add (GST_BIN (pipeline), video_bin); + + setup_dynamic_link (demux, NULL, gst_element_get_pad (v_queue, "sink"), NULL); + + seekable = gst_element_get_pad (a_decoder, "src"); + seekable_pads = g_list_prepend (seekable_pads, seekable); + rate_pads = g_list_prepend (rate_pads, seekable); + rate_pads = + g_list_prepend (rate_pads, gst_element_get_pad (a_decoder, "sink")); + + return pipeline; +} + static GstElement * make_mp3_pipeline (const gchar * location) { GstElement *pipeline; - GstElement *src, *decoder, *audiosink, *queue, *audio_thread; + GstElement *src, *decoder, *osssink, *queue; GstPad *seekable; pipeline = gst_pipeline_new ("app"); @@ -316,24 +503,21 @@ make_mp3_pipeline (const gchar * location) src = gst_element_factory_make_or_warn (SOURCE, "src"); decoder = gst_element_factory_make_or_warn ("mad", "dec"); queue = gst_element_factory_make_or_warn ("queue", "queue"); - audiosink = gst_element_factory_make_or_warn (DEFAULT_AUDIOSINK, "sink"); + osssink = gst_element_factory_make_or_warn ("osssink", "sink"); - audio_thread = gst_thread_new ("a_decoder_thread"); - - seekable_elements = g_list_prepend (seekable_elements, audiosink); + seekable_elements = g_list_prepend (seekable_elements, osssink); g_object_set (G_OBJECT (src), "location", location, NULL); - g_object_set (G_OBJECT (audiosink), "fragment", 0x00180008, NULL); + g_object_set (G_OBJECT (osssink), "fragment", 0x00180008, NULL); gst_bin_add (GST_BIN (pipeline), src); gst_bin_add (GST_BIN (pipeline), decoder); - gst_bin_add (GST_BIN (audio_thread), queue); - gst_bin_add (GST_BIN (audio_thread), audiosink); - gst_bin_add (GST_BIN (pipeline), audio_thread); + gst_bin_add (GST_BIN (pipeline), queue); + gst_bin_add (GST_BIN (pipeline), osssink); gst_element_link (src, decoder); gst_element_link (decoder, queue); - gst_element_link (queue, audiosink); + gst_element_link (queue, osssink); seekable = gst_element_get_pad (queue, "src"); seekable_pads = g_list_prepend (seekable_pads, seekable); @@ -348,8 +532,7 @@ make_avi_pipeline (const gchar * location) { GstElement *pipeline, *audio_bin, *video_bin; GstElement *src, *demux, *a_decoder, *v_decoder, *audiosink, *videosink; - GstElement *a_queue = NULL, *audio_thread = NULL, *v_queue = - NULL, *video_thread = NULL; + GstElement *a_queue = NULL, *v_queue = NULL; GstPad *seekable; pipeline = gst_pipeline_new ("app"); @@ -366,16 +549,14 @@ make_avi_pipeline (const gchar * location) audio_bin = gst_bin_new ("a_decoder_bin"); a_decoder = gst_element_factory_make_or_warn ("mad", "a_dec"); - audio_thread = gst_thread_new ("a_decoder_thread"); - audiosink = gst_element_factory_make_or_warn (DEFAULT_AUDIOSINK, "a_sink"); + audiosink = gst_element_factory_make_or_warn ("osssink", "a_sink"); //g_object_set (G_OBJECT (audiosink), "fragment", 0x00180008, NULL); a_queue = gst_element_factory_make_or_warn ("queue", "a_queue"); gst_element_link (a_decoder, a_queue); gst_element_link (a_queue, audiosink); gst_bin_add (GST_BIN (audio_bin), a_decoder); - gst_bin_add (GST_BIN (audio_bin), audio_thread); - gst_bin_add (GST_BIN (audio_thread), a_queue); - gst_bin_add (GST_BIN (audio_thread), audiosink); + gst_bin_add (GST_BIN (audio_bin), a_queue); + gst_bin_add (GST_BIN (audio_bin), audiosink); gst_element_set_state (audio_bin, GST_STATE_PAUSED); setup_dynamic_link (demux, "audio_00", gst_element_get_pad (a_decoder, @@ -391,8 +572,7 @@ make_avi_pipeline (const gchar * location) //v_decoder = gst_element_factory_make_or_warn ("identity", "v_dec"); //v_decoder = gst_element_factory_make_or_warn ("windec", "v_dec"); v_decoder = gst_element_factory_make_or_warn ("ffmpegdecall", "v_dec"); - video_thread = gst_thread_new ("v_decoder_thread"); - videosink = gst_element_factory_make_or_warn (DEFAULT_VIDEOSINK, "v_sink"); + videosink = gst_element_factory_make_or_warn ("ximagesink", "v_sink"); //videosink = gst_element_factory_make_or_warn ("fakesink", "v_sink"); //g_object_set (G_OBJECT (videosink), "sync", TRUE, NULL); v_queue = gst_element_factory_make_or_warn ("queue", "v_queue"); @@ -400,9 +580,8 @@ make_avi_pipeline (const gchar * location) gst_element_link (v_decoder, v_queue); gst_element_link (v_queue, videosink); gst_bin_add (GST_BIN (video_bin), v_decoder); - gst_bin_add (GST_BIN (video_bin), video_thread); - gst_bin_add (GST_BIN (video_thread), v_queue); - gst_bin_add (GST_BIN (video_thread), videosink); + gst_bin_add (GST_BIN (video_bin), v_queue); + gst_bin_add (GST_BIN (video_bin), videosink); gst_element_set_state (video_bin, GST_STATE_PAUSED); @@ -424,7 +603,7 @@ make_mpeg_pipeline (const gchar * location) GstElement *pipeline, *audio_bin, *video_bin; GstElement *src, *demux, *a_decoder, *v_decoder, *v_filter; GstElement *audiosink, *videosink; - GstElement *a_queue, *audio_thread, *v_queue, *video_thread; + GstElement *a_queue, *v_queue; GstPad *seekable; pipeline = gst_pipeline_new ("app"); @@ -443,16 +622,14 @@ make_mpeg_pipeline (const gchar * location) audio_bin = gst_bin_new ("a_decoder_bin"); a_decoder = gst_element_factory_make_or_warn ("mad", "a_dec"); - audio_thread = gst_thread_new ("a_decoder_thread"); a_queue = gst_element_factory_make_or_warn ("queue", "a_queue"); - audiosink = gst_element_factory_make_or_warn (DEFAULT_AUDIOSINK, "a_sink"); + audiosink = gst_element_factory_make_or_warn ("osssink", "a_sink"); g_object_set (G_OBJECT (audiosink), "fragment", 0x00180008, NULL); gst_element_link (a_decoder, a_queue); gst_element_link (a_queue, audiosink); gst_bin_add (GST_BIN (audio_bin), a_decoder); - gst_bin_add (GST_BIN (audio_bin), audio_thread); - gst_bin_add (GST_BIN (audio_thread), a_queue); - gst_bin_add (GST_BIN (audio_thread), audiosink); + gst_bin_add (GST_BIN (audio_bin), a_queue); + gst_bin_add (GST_BIN (audio_bin), audiosink); setup_dynamic_link (demux, "audio_00", gst_element_get_pad (a_decoder, "sink"), audio_bin); @@ -465,16 +642,15 @@ make_mpeg_pipeline (const gchar * location) video_bin = gst_bin_new ("v_decoder_bin"); v_decoder = gst_element_factory_make_or_warn ("mpeg2dec", "v_dec"); - video_thread = gst_thread_new ("v_decoder_thread"); //g_object_set (G_OBJECT (video_thread), "priority", 2, NULL); v_queue = gst_element_factory_make_or_warn ("queue", "v_queue"); v_filter = gst_element_factory_make_or_warn ("ffmpegcolorspace", "v_filter"); - videosink = gst_element_factory_make_or_warn (DEFAULT_VIDEOSINK, "v_sink"); + videosink = gst_element_factory_make_or_warn ("ximagesink", "v_sink"); gst_element_link_many (v_decoder, v_queue, v_filter, NULL); gst_element_link (v_filter, videosink); - gst_bin_add_many (GST_BIN (video_bin), v_decoder, video_thread, NULL); - gst_bin_add_many (GST_BIN (video_thread), v_queue, v_filter, videosink, NULL); + gst_bin_add_many (GST_BIN (video_bin), v_decoder, NULL); + gst_bin_add_many (GST_BIN (video_bin), v_queue, v_filter, videosink, NULL); setup_dynamic_link (demux, "video_00", gst_element_get_pad (v_decoder, "sink"), video_bin); @@ -494,7 +670,7 @@ make_mpegnt_pipeline (const gchar * location) GstElement *pipeline, *audio_bin, *video_bin; GstElement *src, *demux, *a_decoder, *v_decoder, *v_filter; GstElement *audiosink, *videosink; - GstElement *a_queue, *audio_thread; + GstElement *a_queue; GstPad *seekable; pipeline = gst_pipeline_new ("app"); @@ -513,17 +689,15 @@ make_mpegnt_pipeline (const gchar * location) audio_bin = gst_bin_new ("a_decoder_bin"); a_decoder = gst_element_factory_make_or_warn ("mad", "a_dec"); - audio_thread = gst_thread_new ("a_decoder_thread"); a_queue = gst_element_factory_make_or_warn ("queue", "a_queue"); - audiosink = gst_element_factory_make_or_warn (DEFAULT_AUDIOSINK, "a_sink"); + audiosink = gst_element_factory_make_or_warn ("osssink", "a_sink"); //g_object_set (G_OBJECT (audiosink), "fragment", 0x00180008, NULL); g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL); gst_element_link (a_decoder, a_queue); gst_element_link (a_queue, audiosink); gst_bin_add (GST_BIN (audio_bin), a_decoder); - gst_bin_add (GST_BIN (audio_bin), audio_thread); - gst_bin_add (GST_BIN (audio_thread), a_queue); - gst_bin_add (GST_BIN (audio_thread), audiosink); + gst_bin_add (GST_BIN (audio_bin), a_queue); + gst_bin_add (GST_BIN (audio_bin), audiosink); setup_dynamic_link (demux, "audio_00", gst_element_get_pad (a_decoder, "sink"), audio_bin); @@ -537,7 +711,7 @@ make_mpegnt_pipeline (const gchar * location) video_bin = gst_bin_new ("v_decoder_bin"); v_decoder = gst_element_factory_make_or_warn ("mpeg2dec", "v_dec"); v_filter = gst_element_factory_make_or_warn ("ffmpegcolorspace", "v_filter"); - videosink = gst_element_factory_make_or_warn (DEFAULT_VIDEOSINK, "v_sink"); + videosink = gst_element_factory_make_or_warn ("ximagesink", "v_sink"); gst_element_link_many (v_decoder, v_filter, videosink, NULL); gst_bin_add_many (GST_BIN (video_bin), v_decoder, v_filter, videosink, NULL); @@ -554,45 +728,15 @@ make_mpegnt_pipeline (const gchar * location) return pipeline; } -static GstCaps * -fixate (GstPad * pad, const GstCaps * in_caps, gpointer data) -{ - GstCaps *caps; - GstStructure *s; - - if (gst_caps_get_size (in_caps) > 1) - return NULL; - - /* nothing if fixed already */ - s = gst_caps_get_structure (in_caps, 0); - if (gst_structure_has_field_typed (s, "width", G_TYPE_INT) && - gst_structure_has_field_typed (s, "height", G_TYPE_INT) && - gst_structure_has_field_typed (s, "framerate", G_TYPE_DOUBLE)) - return NULL; - - /* fixate */ - caps = gst_caps_copy (in_caps); - s = gst_caps_get_structure (caps, 0); - gst_caps_structure_fixate_field_nearest_int (s, "width", 200); - gst_caps_structure_fixate_field_nearest_int (s, "height", 150); - gst_caps_structure_fixate_field_nearest_double (s, "framerate", 10.0); - - return caps; -} - static GstElement * make_playerbin_pipeline (const gchar * location) { - GstElement *player, *vis; + GstElement *player; player = gst_element_factory_make ("playbin", "player"); - vis = gst_element_factory_make ("synaesthesia", "vis"); g_assert (player); - g_assert (vis); - g_signal_connect (gst_element_get_pad (vis, "src"), "fixate", - G_CALLBACK (fixate), NULL); - g_object_set (G_OBJECT (player), "uri", location, "vis-plugin", vis, NULL); + g_object_set (G_OBJECT (player), "uri", location, NULL); seekable_elements = g_list_prepend (seekable_elements, player); @@ -791,7 +935,7 @@ update_scale (gpointer data) gboolean res; duration = 0; - clock = gst_bin_get_clock (GST_BIN (pipeline)); + clock = gst_pipeline_get_clock (GST_PIPELINE (pipeline)); if (elem_seek) { if (seekable_elements) { @@ -843,36 +987,21 @@ update_scale (gpointer data) return TRUE; } +static void do_seek (GtkWidget * widget); + +#ifdef SCRUB static gboolean -iterate (gpointer data) -{ - gboolean res; - - if (!GST_FLAG_IS_SET (GST_OBJECT (data), GST_BIN_SELF_SCHEDULABLE)) { - res = gst_bin_iterate (GST_BIN (data)); - } else { - g_usleep (500); - res = gst_element_get_state (GST_ELEMENT (data)) == GST_STATE_PLAYING; - } - - if (!res) { - gtk_timeout_remove (update_id); - g_print ("stopping iterations\n"); - } - return res; -} - -static gboolean -start_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data) +end_scrub (GtkWidget * widget) { gst_element_set_state (pipeline, GST_STATE_PAUSED); - gtk_timeout_remove (update_id); + seek_timeout_id = 0; return FALSE; } +#endif -static gboolean -stop_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data) +static void +do_seek (GtkWidget * widget) { gint64 real = gtk_range_get_value (GTK_RANGE (widget)) * duration / 100; gboolean res; @@ -884,8 +1013,8 @@ stop_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data) while (walk) { GstPad *seekable = GST_PAD (walk->data); - g_print ("seek to %" G_GINT64_FORMAT " on pad %s:%s\n", real, - GST_DEBUG_PAD_NAME (seekable)); + g_print ("seek to %" GST_TIME_FORMAT " on pad %s:%s\n", + GST_TIME_ARGS (real), GST_DEBUG_PAD_NAME (seekable)); s_event = gst_event_new_seek (GST_FORMAT_TIME | GST_SEEK_METHOD_SET | GST_SEEK_FLAG_FLUSH, real); @@ -912,8 +1041,57 @@ stop_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data) } } + GST_PIPELINE (pipeline)->stream_time = real; +} + +static void +seek_cb (GtkWidget * widget) +{ +#ifdef SCRUB + /* If the timer hasn't expired yet, then the pipeline is running */ + if (seek_timeout_id != 0) { + gst_element_set_state (pipeline, GST_STATE_PAUSED); + } +#endif + + do_seek (widget); + +#ifdef SCRUB gst_element_set_state (pipeline, GST_STATE_PLAYING); - gtk_idle_add ((GtkFunction) iterate, pipeline); + + if (seek_timeout_id == 0) { + seek_timeout_id = + gtk_timeout_add (SCRUB_TIME, (GSourceFunc) end_scrub, widget); + } +#endif +} + +static gboolean +start_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data) +{ + gst_element_set_state (pipeline, GST_STATE_PAUSED); + gtk_timeout_remove (update_id); + + if (changed_id == 0) { + changed_id = gtk_signal_connect (GTK_OBJECT (hscale), + "value_changed", G_CALLBACK (seek_cb), pipeline); + } + + return FALSE; +} + +static gboolean +stop_seek (GtkWidget * widget, gpointer user_data) +{ + g_signal_handler_disconnect (GTK_OBJECT (hscale), changed_id); + changed_id = 0; + if (seek_timeout_id != 0) { + gtk_timeout_remove (seek_timeout_id); + seek_timeout_id = 0; + /* Still scrubbing, so the pipeline is already playing */ + } else + gst_element_set_state (pipeline, GST_STATE_PLAYING); + update_id = gtk_timeout_add (UPDATE_INTERVAL, (GtkFunction) update_scale, pipeline); @@ -923,9 +1101,11 @@ stop_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data) static void play_cb (GtkButton * button, gpointer data) { - if (gst_element_get_state (pipeline) != GST_STATE_PLAYING) { + GstElementState state; + + gst_element_get_state (pipeline, &state, NULL, NULL); + if (state != GST_STATE_PLAYING) { gst_element_set_state (pipeline, GST_STATE_PLAYING); - gtk_idle_add ((GtkFunction) iterate, pipeline); update_id = gtk_timeout_add (UPDATE_INTERVAL, (GtkFunction) update_scale, pipeline); } @@ -934,7 +1114,10 @@ play_cb (GtkButton * button, gpointer data) static void pause_cb (GtkButton * button, gpointer data) { - if (gst_element_get_state (pipeline) != GST_STATE_PAUSED) { + GstElementState state; + + gst_element_get_state (pipeline, &state, NULL, NULL); + if (state != GST_STATE_PAUSED) { gst_element_set_state (pipeline, GST_STATE_PAUSED); gtk_timeout_remove (update_id); } @@ -943,7 +1126,10 @@ pause_cb (GtkButton * button, gpointer data) static void stop_cb (GtkButton * button, gpointer data) { - if (gst_element_get_state (pipeline) != GST_STATE_READY) { + GstElementState state; + + gst_element_get_state (pipeline, &state, NULL, NULL); + if (state != GST_STATE_READY) { gst_element_set_state (pipeline, GST_STATE_READY); gtk_adjustment_set_value (adjustment, 0.0); gtk_timeout_remove (update_id); @@ -963,6 +1149,9 @@ static Pipeline pipelines[] = { {"mpeg1", make_mpeg_pipeline}, {"mpegparse", make_parse_pipeline}, {"vorbis", make_vorbis_pipeline}, + {"theora", make_theora_pipeline}, + {"ogg/v/t", make_vorbis_theora_pipeline}, + {"avi/msmpeg4v3/mp3", make_avi_msmpeg4v3_mp3_pipeline}, {"sid", make_sid_pipeline}, {"flac", make_flac_pipeline}, {"wav", make_wav_pipeline}, @@ -1065,18 +1254,13 @@ main (int argc, char **argv) if (verbose) { g_signal_connect (pipeline, "deep_notify", - G_CALLBACK (gst_element_default_deep_notify), NULL); + G_CALLBACK (gst_object_default_deep_notify), NULL); } - g_signal_connect (pipeline, "error", G_CALLBACK (gst_element_default_error), - NULL); - gtk_main (); gst_element_set_state (pipeline, GST_STATE_NULL); - //gst_object_unref (GST_OBJECT (pipeline)); - - //g_mem_chunk_info(); + gst_object_unref (GST_OBJECT (pipeline)); return 0; } diff --git a/tests/examples/seek/spider_seek.c b/tests/examples/seek/spider_seek.c deleted file mode 100644 index d63d3bde84..0000000000 --- a/tests/examples/seek/spider_seek.c +++ /dev/null @@ -1,379 +0,0 @@ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif -#include -#include -#include -#include -#include - -static GList *rate_pads = NULL; -static GList *seekable_elements = NULL; - -static GstElement *pipeline; -static GtkAdjustment *adjustment; -static gboolean stats = FALSE; -static guint64 duration; - -static guint update_id; - -//#define SOURCE "gnomevfssrc" -#define SOURCE "filesrc" - -#define UPDATE_INTERVAL 500 - -static GstElement * -make_spider_pipeline (const gchar * location, gboolean thread) -{ - GstElement *pipeline; - GstElement *src, *decoder, *audiosink, *videosink, *a_thread, *v_thread, - *a_queue, *v_queue; - - if (thread) { - pipeline = gst_thread_new ("app"); - } else { - pipeline = gst_pipeline_new ("app"); - } - - - src = gst_element_factory_make (SOURCE, "src"); - decoder = gst_element_factory_make ("spider", "decoder"); - a_thread = gst_thread_new ("a_thread"); - a_queue = gst_element_factory_make ("queue", "a_queue"); - audiosink = gst_element_factory_make (DEFAULT_AUDIOSINK, "a_sink"); - //g_object_set (G_OBJECT (audiosink), "fragment", 0x00180008, NULL); - - v_thread = gst_thread_new ("v_thread"); - v_queue = gst_element_factory_make ("queue", "v_queue"); - videosink = gst_element_factory_make (DEFAULT_VIDEOSINK, "v_sink"); - //g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL); - - g_object_set (G_OBJECT (src), "location", location, NULL); - - gst_bin_add (GST_BIN (pipeline), src); - gst_bin_add (GST_BIN (pipeline), decoder); - gst_bin_add (GST_BIN (a_thread), a_queue); - gst_bin_add (GST_BIN (a_thread), audiosink); - gst_bin_add (GST_BIN (v_thread), v_queue); - gst_bin_add (GST_BIN (v_thread), videosink); - gst_bin_add (GST_BIN (pipeline), a_thread); - gst_bin_add (GST_BIN (pipeline), v_thread); - - gst_element_link (src, decoder); - gst_element_link (v_queue, videosink); - gst_element_link (decoder, v_queue); - gst_element_link (a_queue, audiosink); - gst_element_link (decoder, a_queue); - - seekable_elements = g_list_prepend (seekable_elements, videosink); - seekable_elements = g_list_prepend (seekable_elements, audiosink); - rate_pads = - g_list_prepend (rate_pads, gst_element_get_pad (audiosink, "sink")); - rate_pads = - g_list_prepend (rate_pads, gst_element_get_pad (videosink, "sink")); - - return pipeline; -} - -static gchar * -format_value (GtkScale * scale, gdouble value) -{ - gint64 real; - gint64 seconds; - gint64 subseconds; - - real = value * duration / 100; - seconds = (gint64) real / GST_SECOND; - subseconds = (gint64) real / (GST_SECOND / 100); - - return g_strdup_printf ("%02" G_GINT64_FORMAT ":%02" G_GINT64_FORMAT ":%02" - G_GINT64_FORMAT, seconds / 60, seconds % 60, subseconds % 100); -} - -typedef struct -{ - const gchar *name; - const GstFormat format; -} -seek_format; - -static seek_format seek_formats[] = { - {"tim", GST_FORMAT_TIME}, - {"byt", GST_FORMAT_BYTES}, - {"buf", GST_FORMAT_BUFFERS}, - {"def", GST_FORMAT_DEFAULT}, - {NULL, 0}, -}; - -G_GNUC_UNUSED static void -query_rates (void) -{ - GList *walk = rate_pads; - - while (walk) { - GstPad *pad = GST_PAD (walk->data); - gint i = 0; - - g_print ("rate/sec %8.8s: ", GST_PAD_NAME (pad)); - while (seek_formats[i].name) { - gint64 value; - GstFormat format; - - format = seek_formats[i].format; - - if (gst_pad_convert (pad, GST_FORMAT_TIME, GST_SECOND, &format, &value)) { - g_print ("%s %13" G_GINT64_FORMAT " | ", seek_formats[i].name, value); - } else { - g_print ("%s %13.13s | ", seek_formats[i].name, "*NA*"); - } - - i++; - } - g_print (" %s:%s\n", GST_DEBUG_PAD_NAME (pad)); - - walk = g_list_next (walk); - } -} - -G_GNUC_UNUSED static void -query_durations () -{ - GList *walk = seekable_elements; - - while (walk) { - GstElement *element = GST_ELEMENT (walk->data); - gint i = 0; - - g_print ("durations %8.8s: ", GST_ELEMENT_NAME (element)); - while (seek_formats[i].name) { - gboolean res; - gint64 value; - GstFormat format; - - format = seek_formats[i].format; - res = gst_element_query (element, GST_QUERY_TOTAL, &format, &value); - if (res) { - g_print ("%s %13" G_GINT64_FORMAT " | ", seek_formats[i].name, value); - } else { - g_print ("%s %13.13s | ", seek_formats[i].name, "*NA*"); - } - i++; - } - g_print (" %s\n", GST_ELEMENT_NAME (element)); - walk = g_list_next (walk); - } -} - -G_GNUC_UNUSED static void -query_positions () -{ - GList *walk = seekable_elements; - - while (walk) { - GstElement *element = GST_ELEMENT (walk->data); - gint i = 0; - - g_print ("positions %8.8s: ", GST_ELEMENT_NAME (element)); - while (seek_formats[i].name) { - gboolean res; - gint64 value; - GstFormat format; - - format = seek_formats[i].format; - res = gst_element_query (element, GST_QUERY_POSITION, &format, &value); - if (res) { - g_print ("%s %13" G_GINT64_FORMAT " | ", seek_formats[i].name, value); - } else { - g_print ("%s %13.13s | ", seek_formats[i].name, "*NA*"); - } - i++; - } - g_print (" %s\n", GST_ELEMENT_NAME (element)); - walk = g_list_next (walk); - } -} - -static gboolean -update_scale (gpointer data) -{ - GstClock *clock; - guint64 position; - GstFormat format = GST_FORMAT_TIME; - - duration = 0; - clock = gst_bin_get_clock (GST_BIN (pipeline)); - - if (seekable_elements) { - GstElement *element = GST_ELEMENT (seekable_elements->data); - - gst_element_query (element, GST_QUERY_TOTAL, &format, &duration); - } - position = gst_clock_get_time (clock); - - if (stats) { - g_print ("clock: %13" G_GUINT64_FORMAT " (%s)\n", - position, gst_object_get_name (GST_OBJECT (clock))); - query_durations (); - query_positions (); - query_rates (); - } - if (duration > 0) { - gtk_adjustment_set_value (adjustment, position * 100.0 / duration); - } - - return TRUE; -} - -static gboolean -iterate (gpointer data) -{ - gboolean res = TRUE; - - res = gst_bin_iterate (GST_BIN (data)); - if (!res) { - gtk_timeout_remove (update_id); - g_print ("stopping iterations\n"); - } - return res; -} - -static gboolean -start_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data) -{ - gst_element_set_state (pipeline, GST_STATE_PAUSED); - gtk_timeout_remove (update_id); - - return FALSE; -} - -static gboolean -stop_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data) -{ - gint64 real = gtk_range_get_value (GTK_RANGE (widget)) * duration / 100; - gboolean res; - GstEvent *s_event; - GList *walk = seekable_elements; - - while (walk) { - GstElement *seekable = GST_ELEMENT (walk->data); - - g_print ("seek to %" G_GINT64_FORMAT " on element %s\n", real, - GST_ELEMENT_NAME (seekable)); - s_event = - gst_event_new_seek (GST_FORMAT_TIME | GST_SEEK_METHOD_SET | - GST_SEEK_FLAG_FLUSH, real); - - res = gst_element_send_event (seekable, s_event); - - walk = g_list_next (walk); - } - - gst_element_set_state (pipeline, GST_STATE_PLAYING); - if (!GST_FLAG_IS_SET (pipeline, GST_BIN_SELF_SCHEDULABLE)) - gtk_idle_add ((GtkFunction) iterate, pipeline); - update_id = - gtk_timeout_add (UPDATE_INTERVAL, (GtkFunction) update_scale, pipeline); - - return FALSE; -} - -static void -play_cb (GtkButton * button, gpointer data) -{ - if (gst_element_get_state (pipeline) != GST_STATE_PLAYING) { - gst_element_set_state (pipeline, GST_STATE_PLAYING); - if (!GST_FLAG_IS_SET (pipeline, GST_BIN_SELF_SCHEDULABLE)) - gtk_idle_add ((GtkFunction) iterate, pipeline); - update_id = - gtk_timeout_add (UPDATE_INTERVAL, (GtkFunction) update_scale, pipeline); - } -} - -static void -pause_cb (GtkButton * button, gpointer data) -{ - if (gst_element_get_state (pipeline) != GST_STATE_PAUSED) { - gst_element_set_state (pipeline, GST_STATE_PAUSED); - gtk_timeout_remove (update_id); - } -} - -static void -stop_cb (GtkButton * button, gpointer data) -{ - if (gst_element_get_state (pipeline) != GST_STATE_READY) { - gst_element_set_state (pipeline, GST_STATE_READY); - gtk_timeout_remove (update_id); - } -} - -int -main (int argc, char **argv) -{ - GtkWidget *window, *hbox, *vbox, - *play_button, *pause_button, *stop_button, *hscale; - gboolean threaded = FALSE; - struct poptOption options[] = { - {"threaded", 't', POPT_ARG_NONE | POPT_ARGFLAG_STRIP, &threaded, 0, - "Run the pipeline in a toplevel thread", NULL}, - {"stats", 's', POPT_ARG_NONE | POPT_ARGFLAG_STRIP, &stats, 0, - "Show element stats", NULL}, - POPT_TABLEEND - }; - - gst_init_with_popt_table (&argc, &argv, options); - gtk_init (&argc, &argv); - - if (argc != 2) { - g_print ("usage: %s \n", argv[0]); - exit (-1); - } - - pipeline = make_spider_pipeline (argv[1], threaded); - - /* initialize gui elements ... */ - window = gtk_window_new (GTK_WINDOW_TOPLEVEL); - hbox = gtk_hbox_new (FALSE, 0); - vbox = gtk_vbox_new (FALSE, 0); - play_button = gtk_button_new_with_label ("play"); - pause_button = gtk_button_new_with_label ("pause"); - stop_button = gtk_button_new_with_label ("stop"); - - adjustment = - GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.00, 100.0, 0.1, 1.0, 1.0)); - hscale = gtk_hscale_new (adjustment); - gtk_scale_set_digits (GTK_SCALE (hscale), 2); - gtk_range_set_update_policy (GTK_RANGE (hscale), GTK_UPDATE_CONTINUOUS); - - gtk_signal_connect (GTK_OBJECT (hscale), - "button_press_event", G_CALLBACK (start_seek), pipeline); - gtk_signal_connect (GTK_OBJECT (hscale), - "button_release_event", G_CALLBACK (stop_seek), pipeline); - gtk_signal_connect (GTK_OBJECT (hscale), - "format_value", G_CALLBACK (format_value), pipeline); - - /* do the packing stuff ... */ - gtk_window_set_default_size (GTK_WINDOW (window), 96, 96); - gtk_container_add (GTK_CONTAINER (window), vbox); - gtk_container_add (GTK_CONTAINER (vbox), hbox); - gtk_box_pack_start (GTK_BOX (hbox), play_button, FALSE, FALSE, 2); - gtk_box_pack_start (GTK_BOX (hbox), pause_button, FALSE, FALSE, 2); - gtk_box_pack_start (GTK_BOX (hbox), stop_button, FALSE, FALSE, 2); - gtk_box_pack_start (GTK_BOX (vbox), hscale, TRUE, TRUE, 2); - - /* connect things ... */ - g_signal_connect (G_OBJECT (play_button), "clicked", G_CALLBACK (play_cb), - pipeline); - g_signal_connect (G_OBJECT (pause_button), "clicked", G_CALLBACK (pause_cb), - pipeline); - g_signal_connect (G_OBJECT (stop_button), "clicked", G_CALLBACK (stop_cb), - pipeline); - g_signal_connect (G_OBJECT (window), "delete_event", gtk_main_quit, NULL); - - /* show the gui. */ - gtk_widget_show_all (window); - - gtk_main (); - - return 0; -} diff --git a/tests/examples/seek/vorbisfile.c b/tests/examples/seek/vorbisfile.c deleted file mode 100644 index d193642fdc..0000000000 --- a/tests/examples/seek/vorbisfile.c +++ /dev/null @@ -1,266 +0,0 @@ -#include -#include -#include - -static gboolean ready = FALSE; - -struct probe_context -{ - GstElement *pipeline; - GstElement *element; - GstPad *pad; - GstFormat ls_format; - - gint total_ls; - - GstCaps *metadata; - GstCaps *streaminfo; - GstCaps *caps; -}; - -static void -print_caps (GstCaps * caps) -{ - char *s; - - s = gst_caps_to_string (caps); - g_print (" %s\n", s); - g_free (s); -} - -static void -print_format (GstCaps * caps) -{ - char *s; - - s = gst_caps_to_string (caps); - g_print (" format: %s\n", s); - g_free (s); -} - -static void -print_lbs_info (struct probe_context *context, gint stream) -{ - const GstFormat *formats; - - /* FIXME: need a better name here */ - g_print (" stream info:\n"); - - /* report info in all supported formats */ - formats = gst_pad_get_formats (context->pad); - while (*formats) { - const GstFormatDefinition *definition; - gint64 value_start, value_end; - gboolean res; - GstFormat format; - - format = *formats; - formats++; - - if (format == context->ls_format) { - continue; - } - - definition = gst_format_get_details (format); - - /* get start and end position of this stream */ - res = gst_pad_convert (context->pad, - context->ls_format, stream, &format, &value_start); - res &= gst_pad_convert (context->pad, - context->ls_format, stream + 1, &format, &value_end); - - if (res) { - /* substract to get the length */ - value_end -= value_start; - - if (format == GST_FORMAT_TIME) { - value_end /= (GST_SECOND / 100); - g_print (" %s: %" G_GINT64_FORMAT ":%02" G_GINT64_FORMAT ".%02" - G_GINT64_FORMAT "\n", definition->nick, value_end / 6000, - (value_end / 100) % 60, (value_end % 100)); - } else { - g_print (" %s: %" G_GINT64_FORMAT "\n", definition->nick, value_end); - } - } else - g_print (" could not get logical stream %s\n", definition->nick); - - } -} - -static void -deep_notify (GObject * object, GstObject * origin, - GParamSpec * pspec, gpointer data) -{ - struct probe_context *context = (struct probe_context *) data; - GValue value = { 0, }; - - if (!strcmp (pspec->name, "metadata")) { - - g_value_init (&value, pspec->value_type); - g_object_get_property (G_OBJECT (origin), pspec->name, &value); - context->metadata = g_value_peek_pointer (&value); - } else if (!strcmp (pspec->name, "streaminfo")) { - - g_value_init (&value, pspec->value_type); - g_object_get_property (G_OBJECT (origin), pspec->name, &value); - context->streaminfo = g_value_peek_pointer (&value); - } else if (!strcmp (pspec->name, "caps")) { - if (GST_IS_PAD (origin) && GST_PAD (origin) == context->pad) { - g_value_init (&value, pspec->value_type); - g_object_get_property (G_OBJECT (origin), pspec->name, &value); - context->caps = g_value_peek_pointer (&value); - - ready = TRUE; - } - } -} - -static gboolean -collect_logical_stream_properties (struct probe_context *context, gint stream) -{ - GstEvent *event; - gboolean res; - gint count; - - g_print ("info for logical stream %d:\n", stream); - - /* seek to stream */ - event = gst_event_new_seek (context->ls_format | - GST_SEEK_METHOD_SET | GST_SEEK_FLAG_FLUSH, stream); - res = gst_pad_send_event (context->pad, event); - if (!res) { - g_warning ("seek to logical track failed"); - return FALSE; - } - - /* run the pipeline to get the info */ - count = 0; - ready = FALSE; - while (gst_bin_iterate (GST_BIN (context->pipeline)) && !ready) { - count++; - if (count > 10) - break; - } - - print_caps (context->metadata); - print_caps (context->streaminfo); - print_format (context->caps); - print_lbs_info (context, stream); - - g_print ("\n"); - - return TRUE; -} - -static void -collect_stream_properties (struct probe_context *context) -{ - const GstFormat *formats; - - ready = FALSE; - while (gst_bin_iterate (GST_BIN (context->pipeline)) && !ready); - - g_print ("stream info:\n"); - - context->total_ls = -1; - - /* report info in all supported formats */ - formats = gst_pad_get_formats (context->pad); - while (*formats) { - const GstFormatDefinition *definition; - gint64 value; - gboolean res; - GstFormat format; - - format = *formats; - formats++; - - res = gst_pad_query (context->pad, GST_QUERY_TOTAL, &format, &value); - - definition = gst_format_get_details (format); - - if (res) { - if (format == GST_FORMAT_TIME) { - value /= (GST_SECOND / 100); - g_print (" total %s: %" G_GINT64_FORMAT ":%02" G_GINT64_FORMAT ".%02" - G_GINT64_FORMAT "\n", definition->nick, value / 6000, - (value / 100) % 60, (value % 100)); - } else { - if (format == context->ls_format) - context->total_ls = value; - g_print (" total %s: %" G_GINT64_FORMAT "\n", definition->nick, value); - } - } - } - - if (context->total_ls == -1) { - g_warning (" could not get number of logical streams"); - } - g_print ("\n"); -} - -int -main (int argc, char **argv) -{ - GstElement *pipeline; - GstElement *filesrc; - GstElement *vorbisfile; - GstPad *pad; - GstFormat logical_stream_format; - struct probe_context *context; - gint stream; - - gst_init (&argc, &argv); - - if (argc < 2) { - g_print ("usage: %s \n", argv[0]); - return (-1); - } - - pipeline = gst_pipeline_new ("pipeline"); - - filesrc = gst_element_factory_make ("filesrc", "filesrc"); - g_assert (filesrc); - g_object_set (G_OBJECT (filesrc), "location", argv[1], NULL); - - vorbisfile = gst_element_factory_make ("vorbisfile", "vorbisfile"); - //vorbisfile = gst_element_factory_make ("mad", "vorbisfile"); - g_assert (vorbisfile); - - gst_bin_add (GST_BIN (pipeline), filesrc); - gst_bin_add (GST_BIN (pipeline), vorbisfile); - - gst_element_link_pads (filesrc, "src", vorbisfile, "sink"); - - pad = gst_element_get_pad (vorbisfile, "src"); - g_assert (pad); - - logical_stream_format = gst_format_get_by_nick ("logical_stream"); - g_assert (logical_stream_format != 0); - - context = g_new0 (struct probe_context, 1); - context->pipeline = pipeline; - context->element = vorbisfile; - context->pad = pad; - context->ls_format = logical_stream_format; - - g_signal_connect (G_OBJECT (pipeline), "deep_notify", - G_CALLBACK (deep_notify), context); - - gst_element_set_state (pipeline, GST_STATE_PLAYING); - - /* at this point we can inspect the stream */ - collect_stream_properties (context); - - /* loop over all logical streams to get info */ - stream = 0; - while (stream < context->total_ls) { - collect_logical_stream_properties (context, stream); - stream++; - } - - /* stop probe */ - gst_element_set_state (pipeline, GST_STATE_NULL); - - return 0; -} diff --git a/tests/old/examples/seek/Makefile.am b/tests/old/examples/seek/Makefile.am index 582306ac8a..3e7c7886db 100644 --- a/tests/old/examples/seek/Makefile.am +++ b/tests/old/examples/seek/Makefile.am @@ -1,4 +1,4 @@ -examples = seek spider_seek cdplayer cdparanoia vorbisfile playbin chained +examples = seek cdplayer cdparanoia noinst_PROGRAMS = $(examples) diff --git a/tests/old/examples/seek/cdparanoia.c b/tests/old/examples/seek/cdparanoia.c index d0e7bc94ba..f5b8c32631 100644 --- a/tests/old/examples/seek/cdparanoia.c +++ b/tests/old/examples/seek/cdparanoia.c @@ -159,7 +159,7 @@ main (int argc, char **argv) gst_element_link_pads (cdparanoia, "src", audiosink, "sink"); g_signal_connect (G_OBJECT (pipeline), "deep_notify", - G_CALLBACK (gst_element_default_deep_notify), NULL); + G_CALLBACK (gst_object_default_deep_notify), NULL); gst_element_set_state (pipeline, GST_STATE_PAUSED); @@ -184,10 +184,9 @@ main (int argc, char **argv) gst_element_set_state (pipeline, GST_STATE_PLAYING); count = 0; - while (gst_bin_iterate (GST_BIN (pipeline))) { + while (count++ < 500) { get_position_info (cdparanoia); - if (count++ > 500) - break; + g_usleep (G_USEC_PER_SEC / 2); } gst_element_set_state (pipeline, GST_STATE_PAUSED); @@ -202,8 +201,10 @@ main (int argc, char **argv) gst_element_set_state (pipeline, GST_STATE_PLAYING); - while (gst_bin_iterate (GST_BIN (pipeline))) { + count = 0; + while (count++ < 500) { get_position_info (cdparanoia); + g_usleep (G_USEC_PER_SEC / 2); } g_print ("\n"); diff --git a/tests/old/examples/seek/cdplayer.c b/tests/old/examples/seek/cdplayer.c index 21fa302d3f..190f17b029 100644 --- a/tests/old/examples/seek/cdplayer.c +++ b/tests/old/examples/seek/cdplayer.c @@ -125,7 +125,7 @@ update_scale (gpointer data) GstFormat format = GST_FORMAT_TIME; duration = 0; - clock = gst_bin_get_clock (GST_BIN (pipeline)); + clock = gst_pipeline_get_clock (GST_PIPELINE (pipeline)); if (seekable_elements) { GstElement *element = GST_ELEMENT (seekable_elements->data); @@ -148,20 +148,6 @@ update_scale (gpointer data) return TRUE; } -static gboolean -iterate (gpointer data) -{ - gboolean res = TRUE; - - g_print ("iterate\n"); - res = gst_bin_iterate (GST_BIN (data)); - if (!res) { - gtk_timeout_remove (update_id); - g_print ("stopping iterations\n"); - } - return res; -} - static gboolean start_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data) { @@ -194,8 +180,6 @@ stop_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data) } gst_element_set_state (pipeline, GST_STATE_PLAYING); - if (!GST_FLAG_IS_SET (pipeline, GST_BIN_SELF_SCHEDULABLE)) - gtk_idle_add ((GtkFunction) iterate, pipeline); update_id = gtk_timeout_add (UPDATE_INTERVAL, (GtkFunction) update_scale, pipeline); @@ -205,10 +189,11 @@ stop_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data) static void play_cb (GtkButton * button, gpointer data) { - if (gst_element_get_state (pipeline) != GST_STATE_PLAYING) { + GstElementState state; + + gst_element_get_state (pipeline, &state, NULL, NULL); + if (state != GST_STATE_PLAYING) { gst_element_set_state (pipeline, GST_STATE_PLAYING); - if (!GST_FLAG_IS_SET (pipeline, GST_BIN_SELF_SCHEDULABLE)) - gtk_idle_add ((GtkFunction) iterate, pipeline); update_id = gtk_timeout_add (UPDATE_INTERVAL, (GtkFunction) update_scale, pipeline); } @@ -217,7 +202,10 @@ play_cb (GtkButton * button, gpointer data) static void pause_cb (GtkButton * button, gpointer data) { - if (gst_element_get_state (pipeline) != GST_STATE_PAUSED) { + GstElementState state; + + gst_element_get_state (pipeline, &state, NULL, NULL); + if (state != GST_STATE_PAUSED) { gst_element_set_state (pipeline, GST_STATE_PAUSED); gtk_timeout_remove (update_id); } @@ -226,7 +214,10 @@ pause_cb (GtkButton * button, gpointer data) static void stop_cb (GtkButton * button, gpointer data) { - if (gst_element_get_state (pipeline) != GST_STATE_READY) { + GstElementState state; + + gst_element_get_state (pipeline, &state, NULL, NULL); + if (state != GST_STATE_READY) { gst_element_set_state (pipeline, GST_STATE_READY); gtk_timeout_remove (update_id); } @@ -249,9 +240,7 @@ main (int argc, char **argv) pipeline = make_cdaudio_pipeline (); g_signal_connect (pipeline, "deep_notify", - G_CALLBACK (gst_element_default_deep_notify), NULL); - g_signal_connect (pipeline, "error", G_CALLBACK (gst_element_default_error), - NULL); + G_CALLBACK (gst_object_default_deep_notify), NULL); /* initialize gui elements ... */ window = gtk_window_new (GTK_WINDOW_TOPLEVEL); diff --git a/tests/old/examples/seek/playbin.c b/tests/old/examples/seek/playbin.c deleted file mode 100644 index b6ff35b8e2..0000000000 --- a/tests/old/examples/seek/playbin.c +++ /dev/null @@ -1,270 +0,0 @@ -#include -#include -#include -#include -#include - -static GstElement *playbin = NULL; -static GstElement *pipeline; -static guint64 duration; -static GtkAdjustment *adjustment; -static GtkWidget *hscale; -static gboolean verbose = FALSE; - -static guint update_id; - -#define UPDATE_INTERVAL 500 - -static GstElement * -make_playerbin_pipeline (const gchar * location) -{ - playbin = gst_element_factory_make ("playbin", "player"); - g_assert (playbin); - - g_object_set (G_OBJECT (playbin), "uri", location, NULL); - - return playbin; -} - -static gchar * -format_value (GtkScale * scale, gdouble value) -{ - gint64 real; - gint64 seconds; - gint64 subseconds; - - real = value * duration / 100; - seconds = (gint64) real / GST_SECOND; - subseconds = (gint64) real / (GST_SECOND / 100); - - return g_strdup_printf ("%02" G_GINT64_FORMAT ":%02" G_GINT64_FORMAT ":%02" - G_GINT64_FORMAT, seconds / 60, seconds % 60, subseconds % 100); -} - -static gboolean -update_scale (gpointer data) -{ - GstClock *clock; - guint64 position; - GstFormat format = GST_FORMAT_TIME; - gboolean res; - - duration = 0; - clock = gst_bin_get_clock (GST_BIN (pipeline)); - - res = gst_element_query (playbin, GST_QUERY_TOTAL, &format, &duration); - if (!res) - duration = 0; - res = gst_element_query (playbin, GST_QUERY_POSITION, &format, &position); - if (!res) - position = 0; - - if (position >= duration) - duration = position; - - if (duration > 0) { - gtk_adjustment_set_value (adjustment, position * 100.0 / duration); - gtk_widget_queue_draw (hscale); - } - - return TRUE; -} - -static gboolean -iterate (gpointer data) -{ - gboolean res; - - if (!GST_FLAG_IS_SET (GST_OBJECT (data), GST_BIN_SELF_SCHEDULABLE)) { - res = gst_bin_iterate (GST_BIN (data)); - } else { - g_usleep (UPDATE_INTERVAL); - res = gst_element_get_state (GST_ELEMENT (data)) == GST_STATE_PLAYING; - } - - if (!res) { - gtk_timeout_remove (update_id); - g_print ("stopping iterations\n"); - } - return res; -} - -static gboolean -start_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data) -{ - gst_element_set_state (pipeline, GST_STATE_PAUSED); - gtk_timeout_remove (update_id); - - return FALSE; -} - -static gboolean -stop_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data) -{ - gint64 real = gtk_range_get_value (GTK_RANGE (widget)) * duration / 100; - gboolean res; - GstEvent *s_event; - - g_print ("seek to %" G_GINT64_FORMAT " on element %s\n", real, - gst_element_get_name (playbin)); - s_event = - gst_event_new_seek (GST_FORMAT_TIME | GST_SEEK_METHOD_SET | - GST_SEEK_FLAG_FLUSH, real); - - res = gst_element_send_event (playbin, s_event); - - gst_element_set_state (pipeline, GST_STATE_PLAYING); - gtk_idle_add ((GtkFunction) iterate, pipeline); - update_id = - gtk_timeout_add (UPDATE_INTERVAL, (GtkFunction) update_scale, pipeline); - - return FALSE; -} - -static void -print_media_info (GstElement * playbin) -{ - GList *streaminfo; - GList *s; - - g_print ("have media info now\n"); - - /* get info about the stream */ - g_object_get (G_OBJECT (playbin), "stream-info", &streaminfo, NULL); - - for (s = streaminfo; s; s = g_list_next (s)) { - GObject *obj = G_OBJECT (s->data); - gint type; - gboolean mute; - - g_object_get (obj, "type", &type, NULL); - g_object_get (obj, "mute", &mute, NULL); - - g_print ("%d %d\n", type, mute); - } -} - -static void -play_cb (GtkButton * button, gpointer data) -{ - if (gst_element_get_state (pipeline) != GST_STATE_PLAYING) { - GstElementStateReturn res; - - res = gst_element_set_state (pipeline, GST_STATE_PAUSED); - if (res == GST_STATE_SUCCESS) { - print_media_info (playbin); - - res = gst_element_set_state (pipeline, GST_STATE_PLAYING); - gtk_idle_add ((GtkFunction) iterate, pipeline); - update_id = - gtk_timeout_add (UPDATE_INTERVAL, (GtkFunction) update_scale, - pipeline); - } else { - g_print ("failed playing\n"); - } - } -} - -static void -pause_cb (GtkButton * button, gpointer data) -{ - if (gst_element_get_state (pipeline) != GST_STATE_PAUSED) { - gst_element_set_state (pipeline, GST_STATE_PAUSED); - gtk_timeout_remove (update_id); - } -} - -static void -stop_cb (GtkButton * button, gpointer data) -{ - if (gst_element_get_state (pipeline) != GST_STATE_READY) { - gst_element_set_state (pipeline, GST_STATE_READY); - gtk_adjustment_set_value (adjustment, 0.0); - gtk_timeout_remove (update_id); - } -} - -static void -print_usage (int argc, char **argv) -{ - g_print ("usage: %s \n", argv[0]); -} - -int -main (int argc, char **argv) -{ - GtkWidget *window, *hbox, *vbox, *play_button, *pause_button, *stop_button; - struct poptOption options[] = { - {"verbose", 'v', POPT_ARG_NONE | POPT_ARGFLAG_STRIP, &verbose, 0, - "Verbose properties", NULL}, - POPT_TABLEEND - }; - - gst_init_with_popt_table (&argc, &argv, options); - gtk_init (&argc, &argv); - - if (argc != 2) { - print_usage (argc, argv); - exit (-1); - } - - pipeline = make_playerbin_pipeline (argv[1]); - g_assert (pipeline); - - /* initialize gui elements ... */ - window = gtk_window_new (GTK_WINDOW_TOPLEVEL); - hbox = gtk_hbox_new (FALSE, 0); - vbox = gtk_vbox_new (FALSE, 0); - play_button = gtk_button_new_with_label ("play"); - pause_button = gtk_button_new_with_label ("pause"); - stop_button = gtk_button_new_with_label ("stop"); - - adjustment = - GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.00, 100.0, 0.1, 1.0, 1.0)); - hscale = gtk_hscale_new (adjustment); - gtk_scale_set_digits (GTK_SCALE (hscale), 2); - gtk_range_set_update_policy (GTK_RANGE (hscale), GTK_UPDATE_CONTINUOUS); - - gtk_signal_connect (GTK_OBJECT (hscale), - "button_press_event", G_CALLBACK (start_seek), pipeline); - gtk_signal_connect (GTK_OBJECT (hscale), - "button_release_event", G_CALLBACK (stop_seek), pipeline); - gtk_signal_connect (GTK_OBJECT (hscale), - "format_value", G_CALLBACK (format_value), pipeline); - - /* do the packing stuff ... */ - gtk_window_set_default_size (GTK_WINDOW (window), 96, 96); - gtk_container_add (GTK_CONTAINER (window), vbox); - gtk_container_add (GTK_CONTAINER (vbox), hbox); - gtk_box_pack_start (GTK_BOX (hbox), play_button, FALSE, FALSE, 2); - gtk_box_pack_start (GTK_BOX (hbox), pause_button, FALSE, FALSE, 2); - gtk_box_pack_start (GTK_BOX (hbox), stop_button, FALSE, FALSE, 2); - gtk_box_pack_start (GTK_BOX (vbox), hscale, TRUE, TRUE, 2); - - /* connect things ... */ - g_signal_connect (G_OBJECT (play_button), "clicked", G_CALLBACK (play_cb), - pipeline); - g_signal_connect (G_OBJECT (pause_button), "clicked", G_CALLBACK (pause_cb), - pipeline); - g_signal_connect (G_OBJECT (stop_button), "clicked", G_CALLBACK (stop_cb), - pipeline); - g_signal_connect (G_OBJECT (window), "delete_event", gtk_main_quit, NULL); - - /* show the gui. */ - gtk_widget_show_all (window); - - if (verbose) { - g_signal_connect (pipeline, "deep_notify", - G_CALLBACK (gst_element_default_deep_notify), NULL); - } - g_signal_connect (pipeline, "error", G_CALLBACK (gst_element_default_error), - NULL); - - gtk_main (); - - gst_element_set_state (pipeline, GST_STATE_NULL); - - gst_object_unref (GST_OBJECT (pipeline)); - - return 0; -} diff --git a/tests/old/examples/seek/spider_seek.c b/tests/old/examples/seek/spider_seek.c deleted file mode 100644 index d63d3bde84..0000000000 --- a/tests/old/examples/seek/spider_seek.c +++ /dev/null @@ -1,379 +0,0 @@ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif -#include -#include -#include -#include -#include - -static GList *rate_pads = NULL; -static GList *seekable_elements = NULL; - -static GstElement *pipeline; -static GtkAdjustment *adjustment; -static gboolean stats = FALSE; -static guint64 duration; - -static guint update_id; - -//#define SOURCE "gnomevfssrc" -#define SOURCE "filesrc" - -#define UPDATE_INTERVAL 500 - -static GstElement * -make_spider_pipeline (const gchar * location, gboolean thread) -{ - GstElement *pipeline; - GstElement *src, *decoder, *audiosink, *videosink, *a_thread, *v_thread, - *a_queue, *v_queue; - - if (thread) { - pipeline = gst_thread_new ("app"); - } else { - pipeline = gst_pipeline_new ("app"); - } - - - src = gst_element_factory_make (SOURCE, "src"); - decoder = gst_element_factory_make ("spider", "decoder"); - a_thread = gst_thread_new ("a_thread"); - a_queue = gst_element_factory_make ("queue", "a_queue"); - audiosink = gst_element_factory_make (DEFAULT_AUDIOSINK, "a_sink"); - //g_object_set (G_OBJECT (audiosink), "fragment", 0x00180008, NULL); - - v_thread = gst_thread_new ("v_thread"); - v_queue = gst_element_factory_make ("queue", "v_queue"); - videosink = gst_element_factory_make (DEFAULT_VIDEOSINK, "v_sink"); - //g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL); - - g_object_set (G_OBJECT (src), "location", location, NULL); - - gst_bin_add (GST_BIN (pipeline), src); - gst_bin_add (GST_BIN (pipeline), decoder); - gst_bin_add (GST_BIN (a_thread), a_queue); - gst_bin_add (GST_BIN (a_thread), audiosink); - gst_bin_add (GST_BIN (v_thread), v_queue); - gst_bin_add (GST_BIN (v_thread), videosink); - gst_bin_add (GST_BIN (pipeline), a_thread); - gst_bin_add (GST_BIN (pipeline), v_thread); - - gst_element_link (src, decoder); - gst_element_link (v_queue, videosink); - gst_element_link (decoder, v_queue); - gst_element_link (a_queue, audiosink); - gst_element_link (decoder, a_queue); - - seekable_elements = g_list_prepend (seekable_elements, videosink); - seekable_elements = g_list_prepend (seekable_elements, audiosink); - rate_pads = - g_list_prepend (rate_pads, gst_element_get_pad (audiosink, "sink")); - rate_pads = - g_list_prepend (rate_pads, gst_element_get_pad (videosink, "sink")); - - return pipeline; -} - -static gchar * -format_value (GtkScale * scale, gdouble value) -{ - gint64 real; - gint64 seconds; - gint64 subseconds; - - real = value * duration / 100; - seconds = (gint64) real / GST_SECOND; - subseconds = (gint64) real / (GST_SECOND / 100); - - return g_strdup_printf ("%02" G_GINT64_FORMAT ":%02" G_GINT64_FORMAT ":%02" - G_GINT64_FORMAT, seconds / 60, seconds % 60, subseconds % 100); -} - -typedef struct -{ - const gchar *name; - const GstFormat format; -} -seek_format; - -static seek_format seek_formats[] = { - {"tim", GST_FORMAT_TIME}, - {"byt", GST_FORMAT_BYTES}, - {"buf", GST_FORMAT_BUFFERS}, - {"def", GST_FORMAT_DEFAULT}, - {NULL, 0}, -}; - -G_GNUC_UNUSED static void -query_rates (void) -{ - GList *walk = rate_pads; - - while (walk) { - GstPad *pad = GST_PAD (walk->data); - gint i = 0; - - g_print ("rate/sec %8.8s: ", GST_PAD_NAME (pad)); - while (seek_formats[i].name) { - gint64 value; - GstFormat format; - - format = seek_formats[i].format; - - if (gst_pad_convert (pad, GST_FORMAT_TIME, GST_SECOND, &format, &value)) { - g_print ("%s %13" G_GINT64_FORMAT " | ", seek_formats[i].name, value); - } else { - g_print ("%s %13.13s | ", seek_formats[i].name, "*NA*"); - } - - i++; - } - g_print (" %s:%s\n", GST_DEBUG_PAD_NAME (pad)); - - walk = g_list_next (walk); - } -} - -G_GNUC_UNUSED static void -query_durations () -{ - GList *walk = seekable_elements; - - while (walk) { - GstElement *element = GST_ELEMENT (walk->data); - gint i = 0; - - g_print ("durations %8.8s: ", GST_ELEMENT_NAME (element)); - while (seek_formats[i].name) { - gboolean res; - gint64 value; - GstFormat format; - - format = seek_formats[i].format; - res = gst_element_query (element, GST_QUERY_TOTAL, &format, &value); - if (res) { - g_print ("%s %13" G_GINT64_FORMAT " | ", seek_formats[i].name, value); - } else { - g_print ("%s %13.13s | ", seek_formats[i].name, "*NA*"); - } - i++; - } - g_print (" %s\n", GST_ELEMENT_NAME (element)); - walk = g_list_next (walk); - } -} - -G_GNUC_UNUSED static void -query_positions () -{ - GList *walk = seekable_elements; - - while (walk) { - GstElement *element = GST_ELEMENT (walk->data); - gint i = 0; - - g_print ("positions %8.8s: ", GST_ELEMENT_NAME (element)); - while (seek_formats[i].name) { - gboolean res; - gint64 value; - GstFormat format; - - format = seek_formats[i].format; - res = gst_element_query (element, GST_QUERY_POSITION, &format, &value); - if (res) { - g_print ("%s %13" G_GINT64_FORMAT " | ", seek_formats[i].name, value); - } else { - g_print ("%s %13.13s | ", seek_formats[i].name, "*NA*"); - } - i++; - } - g_print (" %s\n", GST_ELEMENT_NAME (element)); - walk = g_list_next (walk); - } -} - -static gboolean -update_scale (gpointer data) -{ - GstClock *clock; - guint64 position; - GstFormat format = GST_FORMAT_TIME; - - duration = 0; - clock = gst_bin_get_clock (GST_BIN (pipeline)); - - if (seekable_elements) { - GstElement *element = GST_ELEMENT (seekable_elements->data); - - gst_element_query (element, GST_QUERY_TOTAL, &format, &duration); - } - position = gst_clock_get_time (clock); - - if (stats) { - g_print ("clock: %13" G_GUINT64_FORMAT " (%s)\n", - position, gst_object_get_name (GST_OBJECT (clock))); - query_durations (); - query_positions (); - query_rates (); - } - if (duration > 0) { - gtk_adjustment_set_value (adjustment, position * 100.0 / duration); - } - - return TRUE; -} - -static gboolean -iterate (gpointer data) -{ - gboolean res = TRUE; - - res = gst_bin_iterate (GST_BIN (data)); - if (!res) { - gtk_timeout_remove (update_id); - g_print ("stopping iterations\n"); - } - return res; -} - -static gboolean -start_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data) -{ - gst_element_set_state (pipeline, GST_STATE_PAUSED); - gtk_timeout_remove (update_id); - - return FALSE; -} - -static gboolean -stop_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data) -{ - gint64 real = gtk_range_get_value (GTK_RANGE (widget)) * duration / 100; - gboolean res; - GstEvent *s_event; - GList *walk = seekable_elements; - - while (walk) { - GstElement *seekable = GST_ELEMENT (walk->data); - - g_print ("seek to %" G_GINT64_FORMAT " on element %s\n", real, - GST_ELEMENT_NAME (seekable)); - s_event = - gst_event_new_seek (GST_FORMAT_TIME | GST_SEEK_METHOD_SET | - GST_SEEK_FLAG_FLUSH, real); - - res = gst_element_send_event (seekable, s_event); - - walk = g_list_next (walk); - } - - gst_element_set_state (pipeline, GST_STATE_PLAYING); - if (!GST_FLAG_IS_SET (pipeline, GST_BIN_SELF_SCHEDULABLE)) - gtk_idle_add ((GtkFunction) iterate, pipeline); - update_id = - gtk_timeout_add (UPDATE_INTERVAL, (GtkFunction) update_scale, pipeline); - - return FALSE; -} - -static void -play_cb (GtkButton * button, gpointer data) -{ - if (gst_element_get_state (pipeline) != GST_STATE_PLAYING) { - gst_element_set_state (pipeline, GST_STATE_PLAYING); - if (!GST_FLAG_IS_SET (pipeline, GST_BIN_SELF_SCHEDULABLE)) - gtk_idle_add ((GtkFunction) iterate, pipeline); - update_id = - gtk_timeout_add (UPDATE_INTERVAL, (GtkFunction) update_scale, pipeline); - } -} - -static void -pause_cb (GtkButton * button, gpointer data) -{ - if (gst_element_get_state (pipeline) != GST_STATE_PAUSED) { - gst_element_set_state (pipeline, GST_STATE_PAUSED); - gtk_timeout_remove (update_id); - } -} - -static void -stop_cb (GtkButton * button, gpointer data) -{ - if (gst_element_get_state (pipeline) != GST_STATE_READY) { - gst_element_set_state (pipeline, GST_STATE_READY); - gtk_timeout_remove (update_id); - } -} - -int -main (int argc, char **argv) -{ - GtkWidget *window, *hbox, *vbox, - *play_button, *pause_button, *stop_button, *hscale; - gboolean threaded = FALSE; - struct poptOption options[] = { - {"threaded", 't', POPT_ARG_NONE | POPT_ARGFLAG_STRIP, &threaded, 0, - "Run the pipeline in a toplevel thread", NULL}, - {"stats", 's', POPT_ARG_NONE | POPT_ARGFLAG_STRIP, &stats, 0, - "Show element stats", NULL}, - POPT_TABLEEND - }; - - gst_init_with_popt_table (&argc, &argv, options); - gtk_init (&argc, &argv); - - if (argc != 2) { - g_print ("usage: %s \n", argv[0]); - exit (-1); - } - - pipeline = make_spider_pipeline (argv[1], threaded); - - /* initialize gui elements ... */ - window = gtk_window_new (GTK_WINDOW_TOPLEVEL); - hbox = gtk_hbox_new (FALSE, 0); - vbox = gtk_vbox_new (FALSE, 0); - play_button = gtk_button_new_with_label ("play"); - pause_button = gtk_button_new_with_label ("pause"); - stop_button = gtk_button_new_with_label ("stop"); - - adjustment = - GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.00, 100.0, 0.1, 1.0, 1.0)); - hscale = gtk_hscale_new (adjustment); - gtk_scale_set_digits (GTK_SCALE (hscale), 2); - gtk_range_set_update_policy (GTK_RANGE (hscale), GTK_UPDATE_CONTINUOUS); - - gtk_signal_connect (GTK_OBJECT (hscale), - "button_press_event", G_CALLBACK (start_seek), pipeline); - gtk_signal_connect (GTK_OBJECT (hscale), - "button_release_event", G_CALLBACK (stop_seek), pipeline); - gtk_signal_connect (GTK_OBJECT (hscale), - "format_value", G_CALLBACK (format_value), pipeline); - - /* do the packing stuff ... */ - gtk_window_set_default_size (GTK_WINDOW (window), 96, 96); - gtk_container_add (GTK_CONTAINER (window), vbox); - gtk_container_add (GTK_CONTAINER (vbox), hbox); - gtk_box_pack_start (GTK_BOX (hbox), play_button, FALSE, FALSE, 2); - gtk_box_pack_start (GTK_BOX (hbox), pause_button, FALSE, FALSE, 2); - gtk_box_pack_start (GTK_BOX (hbox), stop_button, FALSE, FALSE, 2); - gtk_box_pack_start (GTK_BOX (vbox), hscale, TRUE, TRUE, 2); - - /* connect things ... */ - g_signal_connect (G_OBJECT (play_button), "clicked", G_CALLBACK (play_cb), - pipeline); - g_signal_connect (G_OBJECT (pause_button), "clicked", G_CALLBACK (pause_cb), - pipeline); - g_signal_connect (G_OBJECT (stop_button), "clicked", G_CALLBACK (stop_cb), - pipeline); - g_signal_connect (G_OBJECT (window), "delete_event", gtk_main_quit, NULL); - - /* show the gui. */ - gtk_widget_show_all (window); - - gtk_main (); - - return 0; -} diff --git a/tests/old/examples/seek/vorbisfile.c b/tests/old/examples/seek/vorbisfile.c deleted file mode 100644 index d193642fdc..0000000000 --- a/tests/old/examples/seek/vorbisfile.c +++ /dev/null @@ -1,266 +0,0 @@ -#include -#include -#include - -static gboolean ready = FALSE; - -struct probe_context -{ - GstElement *pipeline; - GstElement *element; - GstPad *pad; - GstFormat ls_format; - - gint total_ls; - - GstCaps *metadata; - GstCaps *streaminfo; - GstCaps *caps; -}; - -static void -print_caps (GstCaps * caps) -{ - char *s; - - s = gst_caps_to_string (caps); - g_print (" %s\n", s); - g_free (s); -} - -static void -print_format (GstCaps * caps) -{ - char *s; - - s = gst_caps_to_string (caps); - g_print (" format: %s\n", s); - g_free (s); -} - -static void -print_lbs_info (struct probe_context *context, gint stream) -{ - const GstFormat *formats; - - /* FIXME: need a better name here */ - g_print (" stream info:\n"); - - /* report info in all supported formats */ - formats = gst_pad_get_formats (context->pad); - while (*formats) { - const GstFormatDefinition *definition; - gint64 value_start, value_end; - gboolean res; - GstFormat format; - - format = *formats; - formats++; - - if (format == context->ls_format) { - continue; - } - - definition = gst_format_get_details (format); - - /* get start and end position of this stream */ - res = gst_pad_convert (context->pad, - context->ls_format, stream, &format, &value_start); - res &= gst_pad_convert (context->pad, - context->ls_format, stream + 1, &format, &value_end); - - if (res) { - /* substract to get the length */ - value_end -= value_start; - - if (format == GST_FORMAT_TIME) { - value_end /= (GST_SECOND / 100); - g_print (" %s: %" G_GINT64_FORMAT ":%02" G_GINT64_FORMAT ".%02" - G_GINT64_FORMAT "\n", definition->nick, value_end / 6000, - (value_end / 100) % 60, (value_end % 100)); - } else { - g_print (" %s: %" G_GINT64_FORMAT "\n", definition->nick, value_end); - } - } else - g_print (" could not get logical stream %s\n", definition->nick); - - } -} - -static void -deep_notify (GObject * object, GstObject * origin, - GParamSpec * pspec, gpointer data) -{ - struct probe_context *context = (struct probe_context *) data; - GValue value = { 0, }; - - if (!strcmp (pspec->name, "metadata")) { - - g_value_init (&value, pspec->value_type); - g_object_get_property (G_OBJECT (origin), pspec->name, &value); - context->metadata = g_value_peek_pointer (&value); - } else if (!strcmp (pspec->name, "streaminfo")) { - - g_value_init (&value, pspec->value_type); - g_object_get_property (G_OBJECT (origin), pspec->name, &value); - context->streaminfo = g_value_peek_pointer (&value); - } else if (!strcmp (pspec->name, "caps")) { - if (GST_IS_PAD (origin) && GST_PAD (origin) == context->pad) { - g_value_init (&value, pspec->value_type); - g_object_get_property (G_OBJECT (origin), pspec->name, &value); - context->caps = g_value_peek_pointer (&value); - - ready = TRUE; - } - } -} - -static gboolean -collect_logical_stream_properties (struct probe_context *context, gint stream) -{ - GstEvent *event; - gboolean res; - gint count; - - g_print ("info for logical stream %d:\n", stream); - - /* seek to stream */ - event = gst_event_new_seek (context->ls_format | - GST_SEEK_METHOD_SET | GST_SEEK_FLAG_FLUSH, stream); - res = gst_pad_send_event (context->pad, event); - if (!res) { - g_warning ("seek to logical track failed"); - return FALSE; - } - - /* run the pipeline to get the info */ - count = 0; - ready = FALSE; - while (gst_bin_iterate (GST_BIN (context->pipeline)) && !ready) { - count++; - if (count > 10) - break; - } - - print_caps (context->metadata); - print_caps (context->streaminfo); - print_format (context->caps); - print_lbs_info (context, stream); - - g_print ("\n"); - - return TRUE; -} - -static void -collect_stream_properties (struct probe_context *context) -{ - const GstFormat *formats; - - ready = FALSE; - while (gst_bin_iterate (GST_BIN (context->pipeline)) && !ready); - - g_print ("stream info:\n"); - - context->total_ls = -1; - - /* report info in all supported formats */ - formats = gst_pad_get_formats (context->pad); - while (*formats) { - const GstFormatDefinition *definition; - gint64 value; - gboolean res; - GstFormat format; - - format = *formats; - formats++; - - res = gst_pad_query (context->pad, GST_QUERY_TOTAL, &format, &value); - - definition = gst_format_get_details (format); - - if (res) { - if (format == GST_FORMAT_TIME) { - value /= (GST_SECOND / 100); - g_print (" total %s: %" G_GINT64_FORMAT ":%02" G_GINT64_FORMAT ".%02" - G_GINT64_FORMAT "\n", definition->nick, value / 6000, - (value / 100) % 60, (value % 100)); - } else { - if (format == context->ls_format) - context->total_ls = value; - g_print (" total %s: %" G_GINT64_FORMAT "\n", definition->nick, value); - } - } - } - - if (context->total_ls == -1) { - g_warning (" could not get number of logical streams"); - } - g_print ("\n"); -} - -int -main (int argc, char **argv) -{ - GstElement *pipeline; - GstElement *filesrc; - GstElement *vorbisfile; - GstPad *pad; - GstFormat logical_stream_format; - struct probe_context *context; - gint stream; - - gst_init (&argc, &argv); - - if (argc < 2) { - g_print ("usage: %s \n", argv[0]); - return (-1); - } - - pipeline = gst_pipeline_new ("pipeline"); - - filesrc = gst_element_factory_make ("filesrc", "filesrc"); - g_assert (filesrc); - g_object_set (G_OBJECT (filesrc), "location", argv[1], NULL); - - vorbisfile = gst_element_factory_make ("vorbisfile", "vorbisfile"); - //vorbisfile = gst_element_factory_make ("mad", "vorbisfile"); - g_assert (vorbisfile); - - gst_bin_add (GST_BIN (pipeline), filesrc); - gst_bin_add (GST_BIN (pipeline), vorbisfile); - - gst_element_link_pads (filesrc, "src", vorbisfile, "sink"); - - pad = gst_element_get_pad (vorbisfile, "src"); - g_assert (pad); - - logical_stream_format = gst_format_get_by_nick ("logical_stream"); - g_assert (logical_stream_format != 0); - - context = g_new0 (struct probe_context, 1); - context->pipeline = pipeline; - context->element = vorbisfile; - context->pad = pad; - context->ls_format = logical_stream_format; - - g_signal_connect (G_OBJECT (pipeline), "deep_notify", - G_CALLBACK (deep_notify), context); - - gst_element_set_state (pipeline, GST_STATE_PLAYING); - - /* at this point we can inspect the stream */ - collect_stream_properties (context); - - /* loop over all logical streams to get info */ - stream = 0; - while (stream < context->total_ls) { - collect_logical_stream_properties (context, stream); - stream++; - } - - /* stop probe */ - gst_element_set_state (pipeline, GST_STATE_NULL); - - return 0; -}