diff --git a/ChangeLog b/ChangeLog index 90155d2e9e..4f3187e549 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,53 @@ +2005-01-08 Ronald S. Bultje + + * configure.ac: + * ext/ogg/gstoggdemux.c: (gst_ogg_pad_new): + * ext/ogg/gstogmparse.c: (gst_ogm_text_parse_get_type), + (gst_ogm_text_parse_base_init), (gst_ogm_text_parse_init), + (gst_ogm_parse_get_sink_querytypes), (gst_ogm_parse_sink_convert), + (gst_ogm_parse_sink_query), (gst_ogm_parse_chain), + (gst_ogm_parse_plugin_init): + * ext/pango/gsttextoverlay.c: (gst_textoverlay_linkedpads), + (gst_textoverlay_link), (gst_textoverlay_getcaps), + (gst_textoverlay_event), (gst_textoverlay_video_chain), + (gst_textoverlay_loop), (gst_textoverlay_init), (plugin_init): + * ext/pango/gsttextoverlay.h: + * gst/matroska/matroska-demux.c: (gst_matroska_demux_add_stream), + (gst_matroska_demux_handle_seek_event), + (gst_matroska_demux_sync_streams), + (gst_matroska_demux_parse_blockgroup), + (gst_matroska_demux_subtitle_caps), + (gst_matroska_demux_plugin_init): + * gst/matroska/matroska-ids.h: + * gst/playback/gstdecodebin.c: (close_pad_link): + * gst/playback/gstplaybasebin.c: (gst_play_base_bin_init), + (gen_preroll_element), (remove_groups), (add_stream), + (new_decoded_pad), (setup_subtitles), (gen_source_element), + (setup_source): + * gst/playback/gstplaybasebin.h: + * gst/playback/gstplaybin.c: (gen_text_element), (setup_sinks): + * gst/subparse/Makefile.am: + * gst/subparse/gstsubparse.c: (gst_subparse_get_type), + (gst_subparse_base_init), (gst_subparse_class_init), + (gst_subparse_init), (gst_subparse_formats), + (gst_subparse_eventmask), (gst_subparse_event), + (gst_subparse_handle_event), (convert_encoding), (get_next_line), + (parse_mdvdsub), (parse_mdvdsub_init), (parse_subrip), + (parse_subrip_deinit), (parse_subrip_init), (parse_mpsub), + (parse_mpsub_deinit), (parse_mpsub_init), + (gst_subparse_buffer_format_autodetect), + (gst_subparse_format_autodetect), (gst_subparse_loop), + (gst_subparse_change_state), (gst_subparse_type_find), + (plugin_init): + * gst/subparse/gstsubparse.h: + * gst/typefind/gsttypefindfunctions.c: (ogmtext_type_find), + (plugin_init): + Add subtitle support, .sub parser (supports SRT and MPsub), + OGM text support, Matroska UTF-8 text support, deadlock fixes + all over the place, subtitle awareness in decodebin/playbin + and some fixes to textoverlay to handle subtitles in a stream + correctly. Fixes #100931. + 2005-01-08 Ronald S. Bultje * ext/vorbis/vorbisdec.c: (vorbis_dec_src_query): diff --git a/configure.ac b/configure.ac index f730ddb976..b50d45f0f6 100644 --- a/configure.ac +++ b/configure.ac @@ -408,6 +408,7 @@ GST_PLUGINS_ALL="\ spectrum \ speed \ stereo \ + subparse \ switch \ synaesthesia \ tags \ @@ -1972,6 +1973,7 @@ gst/smpte/Makefile gst/spectrum/Makefile gst/speed/Makefile gst/stereo/Makefile +gst/subparse/Makefile gst/switch/Makefile gst/synaesthesia/Makefile gst/tags/Makefile diff --git a/ext/pango/gsttextoverlay.c b/ext/pango/gsttextoverlay.c index 52f257a7ae..bbd92b02e2 100644 --- a/ext/pango/gsttextoverlay.c +++ b/ext/pango/gsttextoverlay.c @@ -24,6 +24,9 @@ #include #include "gsttextoverlay.h" +GST_DEBUG_CATEGORY_STATIC (pango_debug); +#define GST_CAT_DEFAULT pango_debug + static GstElementDetails textoverlay_details = { "Text Overlay", "Filter/Editor/Video", @@ -224,23 +227,78 @@ render_text (GstTextOverlay * overlay) /* return GST_PAD_LINK_DONE; */ /* } */ - -static GstPadLinkReturn -gst_textoverlay_video_sinkconnect (GstPad * pad, const GstCaps * caps) +static GList * +gst_textoverlay_linkedpads (GstPad * pad) { + GstPad *otherpad; GstTextOverlay *overlay; - GstStructure *structure; overlay = GST_TEXTOVERLAY (gst_pad_get_parent (pad)); + if (pad == overlay->text_sinkpad) + return NULL; + otherpad = (pad == overlay->video_sinkpad) ? + overlay->srcpad : overlay->video_sinkpad; + + return g_list_append (NULL, otherpad); +} + +static GstPadLinkReturn +gst_textoverlay_link (GstPad * pad, const GstCaps * caps) +{ + GstPad *otherpad; + GstTextOverlay *overlay; + GstStructure *structure; + GstPadLinkReturn ret; + + overlay = GST_TEXTOVERLAY (gst_pad_get_parent (pad)); + otherpad = (pad == overlay->video_sinkpad) ? + overlay->srcpad : overlay->video_sinkpad; + + ret = gst_pad_try_set_caps (otherpad, caps); + if (GST_PAD_LINK_FAILED (ret)) + return ret; structure = gst_caps_get_structure (caps, 0); overlay->width = overlay->height = 0; gst_structure_get_int (structure, "width", &overlay->width); gst_structure_get_int (structure, "height", &overlay->height); - return gst_pad_try_set_caps (overlay->srcpad, caps); + return ret; } +static GstCaps * +gst_textoverlay_getcaps (GstPad * pad) +{ + GstPad *otherpad; + GstTextOverlay *overlay; + GstCaps *caps, *rcaps; + const GstCaps *tcaps; + + overlay = GST_TEXTOVERLAY (gst_pad_get_parent (pad)); + otherpad = (pad == overlay->video_sinkpad) ? + overlay->srcpad : overlay->video_sinkpad; + + caps = gst_pad_get_allowed_caps (otherpad); + tcaps = gst_pad_get_pad_template_caps (pad); + rcaps = gst_caps_intersect (caps, tcaps); + gst_caps_free (caps); + + return rcaps; +} + +static gboolean +gst_textoverlay_event (GstPad * pad, GstEvent * event) +{ + GstTextOverlay *overlay = GST_TEXTOVERLAY (gst_pad_get_parent (pad)); + + if (GST_EVENT_TYPE (event) == GST_EVENT_SEEK && + GST_PAD_IS_LINKED (overlay->text_sinkpad)) { + gst_event_ref (event); + gst_pad_send_event (GST_PAD_PEER (overlay->text_sinkpad), event); + } + + return gst_pad_send_event (GST_PAD_PEER (overlay->video_sinkpad), event); +} static void gst_text_overlay_blit_yuv420 (GstTextOverlay * overlay, FT_Bitmap * bitmap, @@ -353,9 +411,9 @@ gst_textoverlay_video_chain (GstPad * pad, GstData * _data) y0 = overlay->y0; switch (overlay->valign) { case GST_TEXT_OVERLAY_VALIGN_BOTTOM: - y0 += overlay->bitmap.rows; + y0 = overlay->height - overlay->bitmap.rows - y0; break; - case GST_TEXT_OVERLAY_VALIGN_BASELINE: + case GST_TEXT_OVERLAY_VALIGN_BASELINE: /* ? */ y0 -= (overlay->bitmap.rows - overlay->baseline_y); break; case GST_TEXT_OVERLAY_VALIGN_TOP: @@ -366,10 +424,10 @@ gst_textoverlay_video_chain (GstPad * pad, GstData * _data) case GST_TEXT_OVERLAY_HALIGN_LEFT: break; case GST_TEXT_OVERLAY_HALIGN_RIGHT: - x0 -= overlay->bitmap.width; + x0 = overlay->width - overlay->bitmap.width - x0; break; case GST_TEXT_OVERLAY_HALIGN_CENTER: - x0 -= overlay->bitmap.width / 2; + x0 = (overlay->width - overlay->bitmap.width) / 2; break; } @@ -379,10 +437,19 @@ gst_textoverlay_video_chain (GstPad * pad, GstData * _data) gst_pad_push (overlay->srcpad, GST_DATA (buf)); } -#define PAST_END(buffer, time) \ - (GST_BUFFER_TIMESTAMP (buffer) != GST_CLOCK_TIME_NONE && \ - GST_BUFFER_DURATION (buffer) != GST_CLOCK_TIME_NONE && \ - GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer) \ +#define GST_DATA_TIMESTAMP(data) \ + (GST_IS_EVENT (data) ? \ + GST_EVENT_TIMESTAMP (GST_EVENT (data)) : \ + GST_BUFFER_TIMESTAMP (GST_BUFFER (data))) +#define GST_DATA_DURATION(data) \ + (GST_IS_EVENT (data) ? \ + gst_event_filler_get_duration (GST_EVENT (data)) : \ + GST_BUFFER_DURATION (GST_BUFFER (data))) + +#define PAST_END(data, time) \ + (GST_DATA_TIMESTAMP (data) != GST_CLOCK_TIME_NONE && \ + GST_DATA_DURATION (data) != GST_CLOCK_TIME_NONE && \ + GST_DATA_TIMESTAMP (data) + GST_DATA_DURATION (data) \ < (time)) static void @@ -396,8 +463,36 @@ gst_textoverlay_loop (GstElement * element) g_return_if_fail (GST_IS_TEXTOVERLAY (element)); overlay = GST_TEXTOVERLAY (element); - video_frame = GST_BUFFER (gst_pad_pull (overlay->video_sinkpad)); + do { + GST_DEBUG ("Attempting to pull next video frame"); + video_frame = GST_BUFFER (gst_pad_pull (overlay->video_sinkpad)); + if (GST_IS_EVENT (video_frame)) { + GstEvent *event = GST_EVENT (video_frame); + GstEventType type = GST_EVENT_TYPE (event); + + gst_pad_event_default (overlay->video_sinkpad, event); + GST_DEBUG ("Received event of type %d", type); + if (type == GST_EVENT_INTERRUPT) + return; + else if (type == GST_EVENT_EOS) { + /* EOS text stream */ + GstData *data = NULL; + + do { + if (data) + gst_data_unref (data); + data = gst_pad_pull (overlay->text_sinkpad); + } while (!GST_IS_EVENT (data) || + GST_EVENT_TYPE (data) == GST_EVENT_EOS); + gst_data_unref (data); + + return; + } + video_frame = NULL; + } + } while (!video_frame); now = GST_BUFFER_TIMESTAMP (video_frame); + GST_DEBUG ("Got video frame, time=%" GST_TIME_FORMAT, GST_TIME_ARGS (now)); /* * This state machine has a bug that can't be resolved easily. @@ -410,49 +505,89 @@ gst_textoverlay_loop (GstElement * element) * buffer timestamps and durations correctly. (I think) */ - while (overlay->next_buffer == NULL) { - GST_DEBUG ("attempting to pull a buffer"); + while ((!overlay->current_data || + PAST_END (overlay->current_data, now)) && + overlay->next_data == NULL) { + GST_DEBUG ("attempting to pull text data"); /* read all text buffers until we get one "in the future" */ if (!GST_PAD_IS_USABLE (overlay->text_sinkpad)) { break; } - overlay->next_buffer = GST_BUFFER (gst_pad_pull (overlay->text_sinkpad)); - if (!overlay->next_buffer) - break; + do { + overlay->next_data = gst_pad_pull (overlay->text_sinkpad); + if (GST_IS_EVENT (overlay->next_data) && + GST_EVENT_TYPE (overlay->next_data) != GST_EVENT_FILLER) { + GstEvent *event = GST_EVENT (overlay->next_data); + GstEventType type = GST_EVENT_TYPE (event); - if (PAST_END (overlay->next_buffer, now)) { - gst_buffer_unref (overlay->next_buffer); - overlay->next_buffer = NULL; + gst_event_unref (event); + if (type == GST_EVENT_EOS) + break; + else if (type == GST_EVENT_INTERRUPT) + return; + overlay->next_data = NULL; + } + } while (!overlay->next_data); + + if (PAST_END (overlay->next_data, now)) { + GST_DEBUG ("Received %s is past end (%" GST_TIME_FORMAT " + %" + GST_TIME_FORMAT " < %" GST_TIME_FORMAT ")", + GST_IS_EVENT (overlay->next_data) ? "event" : "buffer", + GST_TIME_ARGS (GST_DATA_TIMESTAMP (overlay->next_data)), + GST_TIME_ARGS (GST_DATA_DURATION (overlay->next_data)), + GST_TIME_ARGS (now)); + gst_data_unref (overlay->next_data); + overlay->next_data = NULL; + } else { + GST_DEBUG ("Received new text %s of time %" GST_TIME_FORMAT + "and duration %" GST_TIME_FORMAT, + GST_IS_EVENT (overlay->next_data) ? "event" : "buffer", + GST_TIME_ARGS (GST_DATA_TIMESTAMP (overlay->next_data)), + GST_TIME_ARGS (GST_DATA_DURATION (overlay->next_data))); } } - if (overlay->next_buffer && - (GST_BUFFER_TIMESTAMP (overlay->next_buffer) <= now || - GST_BUFFER_TIMESTAMP (overlay->next_buffer) == GST_CLOCK_TIME_NONE)) { - GST_DEBUG ("using new buffer"); + if (overlay->next_data && + (GST_DATA_TIMESTAMP (overlay->next_data) <= now || + GST_DATA_TIMESTAMP (overlay->next_data) == GST_CLOCK_TIME_NONE)) { + GST_DEBUG ("using new %s", + GST_IS_EVENT (overlay->next_data) ? "event" : "buffer"); - if (overlay->current_buffer) { - gst_buffer_unref (overlay->current_buffer); + if (overlay->current_data) { + gst_data_unref (overlay->current_data); } - overlay->current_buffer = overlay->next_buffer; - overlay->next_buffer = NULL; + overlay->current_data = overlay->next_data; + overlay->next_data = NULL; - GST_DEBUG ("rendering '%*s'", - GST_BUFFER_SIZE (overlay->current_buffer), - GST_BUFFER_DATA (overlay->current_buffer)); - pango_layout_set_markup (overlay->layout, - GST_BUFFER_DATA (overlay->current_buffer), - GST_BUFFER_SIZE (overlay->current_buffer)); + if (GST_IS_BUFFER (overlay->current_data)) { + guint size = GST_BUFFER_SIZE (overlay->current_data); + guint8 *data = GST_BUFFER_DATA (overlay->current_data); + + while (size > 0 && + (data[size - 1] == '\r' || + data[size - 1] == '\n' || data[size - 1] == '\0')) + size--; + + GST_DEBUG ("rendering '%*s'", size, + GST_BUFFER_DATA (overlay->current_data)); + /* somehow pango barfs over "\0" buffers... */ + pango_layout_set_markup (overlay->layout, + GST_BUFFER_DATA (overlay->current_data), size); + } else { + GST_DEBUG ("Filler - no data"); + pango_layout_set_markup (overlay->layout, "", 0); + } render_text (overlay); overlay->need_render = FALSE; } - if (overlay->current_buffer && PAST_END (overlay->current_buffer, now)) { - GST_DEBUG ("dropping old buffer"); + if (overlay->current_data && PAST_END (overlay->current_data, now)) { + GST_DEBUG ("dropping old %s", + GST_IS_EVENT (overlay->current_data) ? "event" : "buffer"); - gst_buffer_unref (overlay->current_buffer); - overlay->current_buffer = NULL; + gst_buffer_unref (overlay->current_data); + overlay->current_data = NULL; overlay->need_render = TRUE; } @@ -515,22 +650,30 @@ gst_textoverlay_init (GstTextOverlay * overlay) overlay->video_sinkpad = gst_pad_new_from_template (gst_static_pad_template_get (&video_sink_template_factory), "video_sink"); -/* gst_pad_set_chain_function(overlay->video_sinkpad, gst_textoverlay_video_chain); */ - gst_pad_set_link_function (overlay->video_sinkpad, - gst_textoverlay_video_sinkconnect); + gst_pad_set_link_function (overlay->video_sinkpad, gst_textoverlay_link); + gst_pad_set_getcaps_function (overlay->video_sinkpad, + gst_textoverlay_getcaps); + gst_pad_set_internal_link_function (overlay->video_sinkpad, + gst_textoverlay_linkedpads); gst_element_add_pad (GST_ELEMENT (overlay), overlay->video_sinkpad); /* text sink */ overlay->text_sinkpad = gst_pad_new_from_template (gst_static_pad_template_get (&text_sink_template_factory), "text_sink"); -/* gst_pad_set_link_function(overlay->text_sinkpad, gst_textoverlay_text_sinkconnect); */ + gst_pad_set_internal_link_function (overlay->text_sinkpad, + gst_textoverlay_linkedpads); gst_element_add_pad (GST_ELEMENT (overlay), overlay->text_sinkpad); /* (video) source */ overlay->srcpad = gst_pad_new_from_template (gst_static_pad_template_get (&textoverlay_src_template_factory), "src"); + gst_pad_set_link_function (overlay->srcpad, gst_textoverlay_link); + gst_pad_set_getcaps_function (overlay->srcpad, gst_textoverlay_getcaps); + gst_pad_set_internal_link_function (overlay->srcpad, + gst_textoverlay_linkedpads); + gst_pad_set_event_function (overlay->srcpad, gst_textoverlay_event); gst_element_add_pad (GST_ELEMENT (overlay), overlay->srcpad); overlay->layout = @@ -539,12 +682,14 @@ gst_textoverlay_init (GstTextOverlay * overlay) overlay->halign = GST_TEXT_OVERLAY_HALIGN_CENTER; overlay->valign = GST_TEXT_OVERLAY_VALIGN_BASELINE; - overlay->x0 = overlay->y0 = 0; + overlay->x0 = overlay->y0 = 25; overlay->default_text = g_strdup (""); overlay->need_render = TRUE; gst_element_set_loop_function (GST_ELEMENT (overlay), gst_textoverlay_loop); + + GST_FLAG_SET (overlay, GST_ELEMENT_EVENT_AWARE); } @@ -647,6 +792,9 @@ plugin_init (GstPlugin * plugin) /*texttestsrc_plugin_init(module, plugin); */ /*subparse_plugin_init(module, plugin); */ + + GST_DEBUG_CATEGORY_INIT (pango_debug, "pango", 0, "Pango elements"); + return TRUE; } diff --git a/ext/pango/gsttextoverlay.h b/ext/pango/gsttextoverlay.h index df5b5b5525..669f88d446 100644 --- a/ext/pango/gsttextoverlay.h +++ b/ext/pango/gsttextoverlay.h @@ -54,8 +54,8 @@ struct _GstTextOverlay { GstTextOverlayHAlign halign; gint x0; gint y0; - GstBuffer *current_buffer; - GstBuffer *next_buffer; + GstData *current_data; + GstData *next_data; gchar *default_text; gboolean need_render; }; diff --git a/gst/matroska/matroska-demux.c b/gst/matroska/matroska-demux.c index c816665ce3..ec085cb848 100644 --- a/gst/matroska/matroska-demux.c +++ b/gst/matroska/matroska-demux.c @@ -313,6 +313,7 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux) context->index = demux->num_streams; context->type = 0; /* no type yet */ context->default_duration = 0; + context->pos = 0; demux->num_streams++; /* start with the master */ @@ -1099,7 +1100,9 @@ gst_matroska_demux_handle_seek_event (GstMatroskaDemux * demux) gst_event_ref (event); gst_pad_push (demux->src[i]->pad, GST_DATA (event)); } + demux->src[i]->pos = entry->time; } + demux->pos = entry->time; gst_event_unref (event); @@ -1741,6 +1744,47 @@ gst_matroska_ebmlnum_sint (guint8 * data, guint size, gint64 * num) return res; } +/* + * Mostly used for subtitles. We add void filler data for each + * lagging stream to make sure we don't deadlock. + */ + +static void +gst_matroska_demux_sync_streams (GstMatroskaDemux * demux) +{ + gint stream_nr; + GstMatroskaTrackContext *context; + + GST_DEBUG ("Sync to %" GST_TIME_FORMAT, GST_TIME_ARGS (demux->pos)); + + for (stream_nr = 0; stream_nr < demux->num_streams; stream_nr++) { + context = demux->src[stream_nr]; + if (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE) + continue; + GST_DEBUG ("Checking for resync on stream %d (%" GST_TIME_FORMAT ")", + stream_nr, GST_TIME_ARGS (context->pos)); + + /* does it lag? 1 second is a random treshold... */ + if (context->pos + (GST_SECOND / 2) < demux->pos) { + /* send filler */ + GstEvent *event; + + event = gst_event_new_filler_stamped (context->pos, + demux->pos - context->pos); + context->pos = demux->pos; + + /* sync */ + GST_DEBUG ("Synchronizing stream %d with others by sending filler " + "at time %" GST_TIME_FORMAT " and duration %" GST_TIME_FORMAT + " to time %" GST_TIME_FORMAT, stream_nr, + GST_TIME_ARGS (context->pos), + GST_TIME_ARGS (demux->pos - context->pos), + GST_TIME_ARGS (demux->pos)); + gst_pad_push (context->pad, GST_DATA (event)); + } + } +} + static gboolean gst_matroska_demux_parse_blockgroup (GstMatroskaDemux * demux, guint64 cluster_time) @@ -1982,13 +2026,22 @@ gst_matroska_demux_parse_blockgroup (GstMatroskaDemux * demux, GST_BUFFER_TIMESTAMP (sub) = cluster_time; else GST_BUFFER_TIMESTAMP (sub) = cluster_time + time; + demux->pos = GST_BUFFER_TIMESTAMP (sub); } + gst_matroska_demux_sync_streams (demux); + demux->src[stream]->pos = demux->pos; - /* do all laces have the same lenght? */ + /* FIXME: do all laces have the same lenght? */ if (duration) { GST_BUFFER_DURATION (sub) = duration / laces; + demux->src[stream]->pos += GST_BUFFER_DURATION (sub); } + GST_DEBUG ("Pushing data of size %d for stream %d, time=%" + GST_TIME_FORMAT " and duration=%" GST_TIME_FORMAT, + GST_BUFFER_SIZE (sub), stream, + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (sub)), + GST_TIME_ARGS (GST_BUFFER_DURATION (sub))); gst_pad_push (demux->src[stream]->pad, GST_DATA (sub)); size -= lace_size[n]; @@ -2800,9 +2853,13 @@ static GstCaps * gst_matroska_demux_subtitle_caps (GstMatroskaTrackSubtitleContext * subtitlecontext, const gchar * codec_id, gpointer data, guint size) { + /*GstMatroskaTrackContext *context = + (GstMatroskaTrackContext *) subtitlecontext; */ GstCaps *caps = NULL; - //.. + if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_SUBTITLE_UTF8)) { + caps = gst_caps_new_simple ("text/plain", NULL); + } return caps; } @@ -2862,7 +2919,8 @@ gst_matroska_demux_plugin_init (GstPlugin * plugin) /* FILLME */ NULL,} , *subtitle_id[] = { - /* FILLME */ + GST_MATROSKA_CODEC_ID_SUBTITLE_UTF8, + /* FILLME */ NULL,}; /* this filter needs the riff parser */ diff --git a/gst/matroska/matroska-ids.h b/gst/matroska/matroska-ids.h index 1c425c179a..ec87fa5617 100644 --- a/gst/matroska/matroska-ids.h +++ b/gst/matroska/matroska-ids.h @@ -153,6 +153,8 @@ #define GST_MATROSKA_CODEC_ID_AUDIO_TTA "A_TTA1" /* TODO: AC3-9/10 (?), Real, Musepack, Quicktime */ +#define GST_MATROSKA_CODEC_ID_SUBTITLE_UTF8 "S_TEXT/UTF8" + /* * Matrodka tags. Strings. */ @@ -223,6 +225,7 @@ typedef struct _GstMatroskaTrackContext { guint uid, num; GstMatroskaTrackFlags flags; guint64 default_duration; + guint64 pos; } GstMatroskaTrackContext; typedef struct _GstMatroskaTrackVideoContext {