gst/playback/: Implement subtitles.
authorJulien Moutte <julien@moutte.net>
Mon, 30 Jan 2006 08:11:14 +0000 (08:11 +0000)
committerJulien Moutte <julien@moutte.net>
Mon, 30 Jan 2006 08:11:14 +0000 (08:11 +0000)
Original commit message from CVS:
2006-01-30  Julien MOUTTE  <julien@moutte.net>

* gst/playback/gstplaybasebin.c: (group_commit),
(queue_overrun),
(setup_subtitle), (setup_source), (set_active_source):
* gst/playback/gstplaybin.c: (gst_play_bin_dispose),
(gen_text_element), (gen_audio_element), (gen_vis_element),
(remove_sinks), (add_sink), (setup_sinks): Implement subtitles.

ChangeLog
gst/playback/gstplaybasebin.c
gst/playback/gstplaybin.c

index 4416c93..0c78b4f 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2006-01-30  Julien MOUTTE  <julien@moutte.net>
+
+       * gst/playback/gstplaybasebin.c: (group_commit), (queue_overrun),
+       (setup_subtitle), (setup_source), (set_active_source):
+       * gst/playback/gstplaybin.c: (gst_play_bin_dispose),
+       (gen_text_element), (gen_audio_element), (gen_vis_element),
+       (remove_sinks), (add_sink), (setup_sinks): Implement subtitles.
+
 2006-01-29  Sebastien Moutte  <sebastien@moutte.net>
 
        * gst-libs/gst/audio/audio.h: (GST_CLOCK_TIME_TO_FRAMES)
index d15b963..b485d07 100644 (file)
@@ -408,8 +408,10 @@ group_commit (GstPlayBaseBin * play_base_bin, gboolean fatal, gboolean subtitle)
       sig_id =
           GPOINTER_TO_INT (g_object_get_data (G_OBJECT (element), "signal_id"));
 
-      GST_LOG ("removing preroll signal %s", GST_ELEMENT_NAME (element));
-      g_signal_handler_disconnect (G_OBJECT (element), sig_id);
+      if (sig_id) {
+        GST_LOG ("removing preroll signal %s", GST_ELEMENT_NAME (element));
+        g_signal_handler_disconnect (G_OBJECT (element), sig_id);
+      }
     }
   }
 
@@ -501,10 +503,15 @@ queue_overrun (GstElement * element, GstPlayBaseBin * play_base_bin)
 {
   GST_DEBUG ("queue %s overrun", GST_ELEMENT_NAME (element));
 
-  group_commit (play_base_bin, FALSE, FALSE);
+  group_commit (play_base_bin, FALSE,
+      GST_OBJECT_PARENT (GST_OBJECT_CAST (element)) ==
+      GST_OBJECT (play_base_bin->subtitle));
 
   g_signal_handlers_disconnect_by_func (element,
       G_CALLBACK (queue_overrun), play_base_bin);
+  /* We have disconnected this signal, remove the signal_id from the object
+     data */
+  g_object_set_data (G_OBJECT (element), "signal_id", NULL);
 }
 
 /* Used for time-based buffering. */
@@ -1057,7 +1064,7 @@ setup_subtitle (GstPlayBaseBin * play_base_bin, gchar * sub_uri)
     return NULL;
   subparse = gst_element_factory_make ("decodebin", "subtitle-decoder");
 
-  subbin = gst_bin_new ("subbin");
+  subbin = gst_bin_new ("subtitle-bin");
   gst_bin_add_many (GST_BIN (subbin), source, subparse, NULL);
   gst_element_link (source, subparse);
 
@@ -1193,71 +1200,45 @@ setup_source (GstPlayBaseBin * play_base_bin, gchar ** new_location)
 
   /* do subs */
   if (subbin) {
-#if 0
-    gint sig4, sig5, sig6;
     GstElement *db;
 
     play_base_bin->subtitle = subbin;
     db = gst_bin_get_by_name (GST_BIN (subbin), "subtitle-decoder");
 
-    /* don't add yet, because we will preroll, and subs shouldn't
-     * preroll (we shouldn't preroll more than once source). */
-    gst_element_set_state (subbin, GST_STATE_PAUSED);
-
     /* do type detection, without adding (so no preroll) */
-    g_signal_connect (G_OBJECT (db),
-        "new-decoded-pad", G_CALLBACK (new_decoded_pad), play_base_bin);
+    g_signal_connect (G_OBJECT (db), "new-decoded-pad",
+        G_CALLBACK (new_decoded_pad), play_base_bin);
     g_signal_connect (G_OBJECT (db), "no-more-pads",
         G_CALLBACK (no_more_pads), play_base_bin);
+    g_signal_connect (G_OBJECT (db), "unknown-type",
+        G_CALLBACK (unknown_type), play_base_bin);
 
     if (!play_base_bin->is_stream) {
-      sig4 = g_signal_connect (G_OBJECT (db),
-          "unknown-type", G_CALLBACK (unknown_type), play_base_bin);
-      sig5 = g_signal_connect (G_OBJECT (subbin), "error",
-          G_CALLBACK (thread_error), error);
-
       /* either when the queues are filled or when the decoder element
        * has no more dynamic streams, the cond is unlocked. We can remove
        * the signal handlers then
        */
-      g_mutex_lock (play_base_bin->group_lock);
-      if (gst_element_set_state (subbin, GST_STATE_PLAYING) ==
-          GST_STATE_CHANGE_SUCCESS) {
-        GST_DEBUG ("waiting for first group...");
-        sig6 = g_signal_connect (G_OBJECT (subbin),
-            "state-changed", G_CALLBACK (state_change), play_base_bin);
-        g_cond_wait (play_base_bin->group_cond, play_base_bin->group_lock);
-        GST_DEBUG ("group done !");
-      } else {
-        GST_DEBUG ("state change failed, subtitle cannot be loaded");
-        sig6 = 0;
-      }
-      g_mutex_unlock (play_base_bin->group_lock);
-
-      if (sig6 != 0)
-        g_signal_handler_disconnect (G_OBJECT (subbin), sig6);
-
-      g_signal_handler_disconnect (G_OBJECT (subbin), sig5);
-      g_signal_handler_disconnect (G_OBJECT (db), sig4);
 
       gst_element_set_state (subbin, GST_STATE_PAUSED);
 
+      GROUP_LOCK (play_base_bin);
+      GST_DEBUG ("waiting for first group...");
+      GROUP_WAIT (play_base_bin);
+      GST_DEBUG ("group done !");
+      GROUP_UNLOCK (play_base_bin);
+
       if (!play_base_bin->building_group ||
           play_base_bin->building_group->type[GST_STREAM_TYPE_TEXT - 1].npads ==
           0) {
-        if (*error) {
-          g_error_free (*error);
-          *error = NULL;
-        }
 
         GST_DEBUG ("No subtitle found - ignoring");
-        gst_object_unref (play_base_bin->subtitle);
+        gst_element_set_state (subbin, GST_STATE_NULL);
+        gst_object_unref (GST_OBJECT (play_base_bin->subtitle));
         play_base_bin->subtitle = NULL;
       } else {
         GST_DEBUG ("Subtitle set-up successful");
       }
     }
-#endif
   }
 
   /* now see if the source element emits raw audio/video all by itself,
@@ -1361,6 +1342,10 @@ setup_source (GstPlayBaseBin * play_base_bin, gchar ** new_location)
   g_signal_connect (G_OBJECT (play_base_bin->decoder),
       "unknown-type", G_CALLBACK (unknown_type), play_base_bin);
 
+  if (play_base_bin->subtitle) {
+    gst_bin_add (GST_BIN (play_base_bin), play_base_bin->subtitle);
+  }
+
   play_base_bin->need_rebuild = FALSE;
 
   return TRUE;
@@ -1618,7 +1603,7 @@ set_active_source (GstPlayBaseBin * play_base_bin,
     GST_LOG ("Muting group type: %d", type);
     g_object_set (sel, "active-pad", "", NULL);
   } else {
-    GST_LOG ("Unuting group type: %d", type);
+    GST_LOG ("Unmuting group type: %d", type);
   }
   mute_group_type (group, type, !have_active);
 }
index 049b327..5adc76d 100644 (file)
@@ -265,6 +265,10 @@ gst_play_bin_dispose (GObject * object)
     gst_object_unref (play_bin->pending_visualisation);
     play_bin->pending_visualisation = NULL;
   }
+  if (play_bin->textoverlay_element != NULL) {
+    gst_object_unref (play_bin->textoverlay_element);
+    play_bin->textoverlay_element = NULL;
+  }
   g_free (play_bin->font_desc);
   play_bin->font_desc = NULL;
 
@@ -657,34 +661,53 @@ gen_text_element (GstPlayBin * play_bin)
   GstElement *element, *csp, *overlay, *vbin;
   GstPad *pad;
 
+  /* Create our bin */
+  element = gst_bin_new ("textbin");
+
+  /* Text overlay */
   overlay = gst_element_factory_make ("textoverlay", "overlay");
+
+  /* Create the video rendering bin */
+  vbin = gen_video_element (play_bin);
+
+  /* If no overlay return the video bin */
+  if (!overlay) {
+    GST_WARNING ("No overlay (pango) element, subtitles disabled");
+    return vbin;
+  }
+
+  /* Set some parameters */
   g_object_set (G_OBJECT (overlay),
       "halign", "center", "valign", "bottom", NULL);
-  play_bin->textoverlay_element = overlay;
   if (play_bin->font_desc) {
     g_object_set (G_OBJECT (play_bin->textoverlay_element),
         "font-desc", play_bin->font_desc, NULL);
   }
-  vbin = gen_video_element (play_bin);
-  if (!overlay) {
-    g_warning ("No overlay (pango) element, subtitles disabled");
-    return vbin;
-  }
+
+  /* Take a ref */
+  play_bin->textoverlay_element = GST_ELEMENT (gst_object_ref (overlay));
+
   csp = gst_element_factory_make ("ffmpegcolorspace", "subtitlecsp");
-  element = gst_bin_new ("textbin");
-  gst_element_link_many (csp, overlay, vbin, NULL);
+
+  /* Add our elements */
   gst_bin_add_many (GST_BIN (element), csp, overlay, vbin, NULL);
 
+  /* Link */
+  gst_element_link_pads (csp, "src", overlay, "video_sink");
+  gst_element_link_pads (overlay, "src", vbin, "sink");
+
+  /* Add ghost pads on the subtitle bin */
   pad = gst_element_get_pad (overlay, "text_sink");
-#define gst_element_add_ghost_pad(element, pad, name) \
-    gst_element_add_pad (element, gst_ghost_pad_new (name, pad))
-  gst_element_add_ghost_pad (element, pad, "text_sink");
+  gst_element_add_pad (element, gst_ghost_pad_new ("text_sink", pad));
   gst_object_unref (pad);
 
   pad = gst_element_get_pad (csp, "sink");
-  gst_element_add_ghost_pad (element, pad, "sink");
+  gst_element_add_pad (element, gst_ghost_pad_new ("sink", pad));
   gst_object_unref (pad);
 
+  /* Set state to READY */
+  gst_element_set_state (element, GST_STATE_READY);
+
   return element;
 }
 
@@ -751,7 +774,7 @@ gen_audio_element (GstPlayBin * play_bin)
   gst_element_link_pads (volume, "src", sink, "sink");
 
   pad = gst_element_get_pad (conv, "sink");
-  gst_element_add_ghost_pad (element, pad, "sink");
+  gst_element_add_pad (element, gst_ghost_pad_new ("sink", pad));
   gst_object_unref (pad);
 
   gst_element_set_state (element, GST_STATE_READY);
@@ -850,7 +873,7 @@ gen_vis_element (GstPlayBin * play_bin)
   gst_object_unref (pad);
 
   pad = gst_element_get_pad (tee, "sink");
-  gst_element_add_ghost_pad (element, pad, "sink");
+  gst_element_add_pad (element, gst_ghost_pad_new ("sink", pad));
   gst_object_unref (pad);
 
   return element;
@@ -942,7 +965,10 @@ remove_sinks (GstPlayBin * play_bin)
     play_bin->frame = NULL;
   }
 
-  play_bin->textoverlay_element = NULL;
+  if (play_bin->textoverlay_element) {
+    gst_object_unref (play_bin->textoverlay_element);
+    play_bin->textoverlay_element = NULL;
+  }
 }
 
 /* loop over the streams and set up the pipeline to play this
@@ -955,7 +981,8 @@ remove_sinks (GstPlayBin * play_bin)
  * one can switch the streams.
  */
 static gboolean
-add_sink (GstPlayBin * play_bin, GstElement * sink, GstPad * srcpad)
+add_sink (GstPlayBin * play_bin, GstElement * sink, GstPad * srcpad,
+    GstPad * subtitle_pad)
 {
   GstPad *sinkpad;
   GstPadLinkReturn linkres;
@@ -988,8 +1015,19 @@ add_sink (GstPlayBin * play_bin, GstElement * sink, GstPad * srcpad)
   if (GST_PAD_LINK_FAILED (linkres))
     goto link_failed;
 
+  if (GST_IS_PAD (subtitle_pad)) {
+    sinkpad = gst_element_get_pad (sink, "text_sink");
+    linkres = gst_pad_link (subtitle_pad, sinkpad);
+    gst_object_unref (sinkpad);
+  }
+
+  /* try to link the subtitle pad of the sink to the stream */
+  if (GST_PAD_LINK_FAILED (linkres)) {
+    goto subtitle_failed;
+  }
+
   /* we got the sink succesfully linked, now keep the sink
-   * in out internal list */
+   * in our internal list */
   play_bin->sinks = g_list_prepend (play_bin->sinks, sink);
 
   return TRUE;
@@ -1018,6 +1056,22 @@ link_failed:
     gst_bin_remove (GST_BIN (play_bin), sink);
     return FALSE;
   }
+subtitle_failed:
+  {
+    gchar *capsstr;
+    GstCaps *caps;
+
+    /* could not link this stream */
+    caps = gst_pad_get_caps (subtitle_pad);
+    capsstr = gst_caps_to_string (caps);
+    GST_DEBUG_OBJECT (play_bin,
+        "subtitle link failed when adding sink, caps %s, reason %d", capsstr,
+        linkres);
+    g_free (capsstr);
+    g_free (caps);
+
+    return TRUE;
+  }
 }
 
 static gboolean
@@ -1027,7 +1081,7 @@ setup_sinks (GstPlayBaseBin * play_base_bin, GstPlayBaseGroup * group)
   GList *streaminfo = NULL, *s;
   gboolean need_vis = FALSE;
   gboolean need_text = FALSE;
-  GstPad *textsrcpad = NULL, *textsinkpad = NULL, *pad;
+  GstPad *textsrcpad = NULL, *pad = NULL;
   GstElement *sink;
   gboolean res = TRUE;
 
@@ -1069,30 +1123,71 @@ setup_sinks (GstPlayBaseBin * play_base_bin, GstPlayBaseGroup * group)
       return FALSE;
     pad = gst_element_get_pad (group->type[GST_STREAM_TYPE_AUDIO - 1].preroll,
         "src");
-    res = add_sink (play_bin, sink, pad);
+    res = add_sink (play_bin, sink, pad, NULL);
     gst_object_unref (pad);
   }
 
   /* link video */
   if (group->type[GST_STREAM_TYPE_VIDEO - 1].npads > 0) {
     if (need_text) {
-      sink = gen_text_element (play_bin);
+      GstObject *parent = NULL, *grandparent = NULL;
+      GstPad *ghost = NULL;
 
-      textsinkpad = gst_element_get_pad (sink, "text_sink");
+      sink = gen_text_element (play_bin);
       textsrcpad =
           gst_element_get_pad (group->type[GST_STREAM_TYPE_TEXT - 1].preroll,
           "src");
-      gst_pad_link (textsrcpad, textsinkpad);
-      gst_object_unref (textsinkpad);
-      gst_object_unref (textsrcpad);
+      /* This pad is from subtitle-bin, we need to create a ghost pad to have
+         common grandparents */
+      parent = gst_object_get_parent (GST_OBJECT (textsrcpad));
+      if (!parent) {
+        GST_WARNING_OBJECT (textsrcpad, "subtitle pad has no parent !");
+        gst_object_unref (textsrcpad);
+        textsrcpad = NULL;
+        goto beach;
+      }
+
+      grandparent = gst_object_get_parent (parent);
+      if (!grandparent) {
+        GST_WARNING_OBJECT (textsrcpad, "subtitle pad has no grandparent !");
+        gst_object_unref (parent);
+        gst_object_unref (textsrcpad);
+        textsrcpad = NULL;
+        goto beach;
+      }
+
+      ghost = gst_ghost_pad_new ("text_src", textsrcpad);
+      if (!GST_IS_PAD (ghost)) {
+        GST_WARNING_OBJECT (textsrcpad, "failed creating ghost pad for "
+            "subtitle-bin");
+        gst_object_unref (parent);
+        gst_object_unref (grandparent);
+        gst_object_unref (textsrcpad);
+        textsrcpad = NULL;
+        goto beach;
+      }
+
+      if (gst_element_add_pad (GST_ELEMENT (grandparent), ghost)) {
+        gst_object_unref (textsrcpad);
+        textsrcpad = ghost;
+      } else {
+        GST_WARNING_OBJECT (ghost, "failed adding ghost pad on subtitle-bin");
+        gst_object_unref (ghost);
+        gst_object_unref (textsrcpad);
+        textsrcpad = NULL;
+      }
+
+      gst_object_unref (parent);
+      gst_object_unref (grandparent);
     } else {
       sink = gen_video_element (play_bin);
     }
+  beach:
     if (!sink)
       return FALSE;
     pad = gst_element_get_pad (group->type[GST_STREAM_TYPE_VIDEO - 1].preroll,
         "src");
-    res = add_sink (play_bin, sink, pad);
+    res = add_sink (play_bin, sink, pad, textsrcpad);
     gst_object_unref (pad);
   }