gst/matroska/: Add support for VOBSUB subtitle tracks and zlib-compressed tracks...
authorFrédéric Riss <frederic.riss@gmail.com>
Mon, 4 Sep 2006 16:21:17 +0000 (16:21 +0000)
committerTim-Philipp Müller <tim@centricular.net>
Mon, 4 Sep 2006 16:21:17 +0000 (16:21 +0000)
Original commit message from CVS:
Patch by: Frédéric Riss  <frederic.riss at gmail dot com>
* gst/matroska/matroska-demux.c: (gst_matroska_track_free),
(gst_matroska_demux_reset),
(gst_matroska_demux_read_track_encodings),
(gst_matroska_demux_add_stream), (gst_matroska_decode_buffer),
(gst_matroska_demux_parse_blockgroup_or_simpleblock),
(gst_matroska_demux_subtitle_caps):
* gst/matroska/matroska-ids.h:
Add support for VOBSUB subtitle tracks and zlib-compressed
tracks. Make sure we start on a keyframe after a seek. (#343348)

ChangeLog
gst/matroska/matroska-demux.c
gst/matroska/matroska-ids.h

index 4780d75..2511bc2 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,19 @@
 2006-09-04  Tim-Philipp Müller  <tim at centricular dot net>
 
+       Patch by: Frédéric Riss  <frederic.riss at gmail dot com>
+
+       * gst/matroska/matroska-demux.c: (gst_matroska_track_free),
+       (gst_matroska_demux_reset),
+       (gst_matroska_demux_read_track_encodings),
+       (gst_matroska_demux_add_stream), (gst_matroska_decode_buffer),
+       (gst_matroska_demux_parse_blockgroup_or_simpleblock),
+       (gst_matroska_demux_subtitle_caps):
+       * gst/matroska/matroska-ids.h:
+         Add support for VOBSUB subtitle tracks and zlib-compressed
+         tracks. Make sure we start on a keyframe after a seek. (#343348)
+
+2006-09-04  Tim-Philipp Müller  <tim at centricular dot net>
+
        * gst/matroska/matroska-demux.c: (gst_matroska_demux_push_hdr_buf),
        (gst_matroska_demux_push_flac_codec_priv_data),
        (gst_matroska_demux_push_xiph_codec_priv_data),
index 3d6208d..17120a0 100644 (file)
 #include <gst/riff/riff-ids.h>
 #include <gst/riff/riff-media.h>
 
+#ifdef HAVE_ZLIB
+#include <zlib.h>
+#endif
+
 #include "matroska-demux.h"
 #include "matroska-ids.h"
 
@@ -71,7 +75,8 @@ static GstStaticPadTemplate subtitle_src_templ =
     GST_PAD_SRC,
     GST_PAD_SOMETIMES,
     GST_STATIC_CAPS ("text/plain; application/x-ssa; application/x-ass; "
-        "application/x-usf; application/x-subtitle-unknown")
+        "application/x-usf; video/x-dvd-subpicture; "
+        "application/x-subtitle-unknown")
     );
 
 static void gst_matroska_demux_base_init (GstMatroskaDemuxClass * klass);
@@ -213,6 +218,31 @@ gst_matroska_demux_init (GstMatroskaDemux * demux)
 }
 
 static void
+gst_matroska_track_free (GstMatroskaTrackContext * track)
+{
+  g_free (track->codec_id);
+  g_free (track->codec_name);
+  g_free (track->name);
+  g_free (track->language);
+  g_free (track->codec_priv);
+
+  if (track->encodings != NULL) {
+    int i;
+
+    for (i = 0; i < track->encodings->len; ++i) {
+      GstMatroskaTrackEncoding *enc = &g_array_index (track->encodings,
+          GstMatroskaTrackEncoding,
+          i);
+
+      g_free (enc->comp_settings);
+    }
+    g_array_free (track->encodings, TRUE);
+  }
+
+  g_free (track);
+}
+
+static void
 gst_matroska_demux_reset (GstElement * element)
 {
   GstMatroskaDemux *demux = GST_MATROSKA_DEMUX (element);
@@ -228,12 +258,7 @@ gst_matroska_demux_reset (GstElement * element)
         gst_element_remove_pad (GST_ELEMENT (demux), demux->src[i]->pad);
       }
       gst_caps_replace (&demux->src[i]->caps, NULL);
-      g_free (demux->src[i]->codec_id);
-      g_free (demux->src[i]->codec_name);
-      g_free (demux->src[i]->name);
-      g_free (demux->src[i]->language);
-      g_free (demux->src[i]->codec_priv);
-      g_free (demux->src[i]);
+      gst_matroska_track_free (demux->src[i]);
       demux->src[i] = NULL;
     }
   }
@@ -289,6 +314,181 @@ gst_matroska_demux_stream_from_num (GstMatroskaDemux * demux, guint track_num)
 }
 
 static gboolean
+gst_matroska_demux_read_track_encodings (GstEbmlRead * ebml,
+    GstMatroskaDemux * demux, GstMatroskaTrackContext * context)
+{
+  gboolean res = TRUE;
+  guint32 id;
+
+  if (!gst_ebml_read_master (ebml, &id))
+    return FALSE;
+
+  context->encodings =
+      g_array_sized_new (FALSE, FALSE, sizeof (GstMatroskaTrackEncoding), 1);
+
+  while (res) {
+    if (!gst_ebml_peek_id (ebml, &demux->level_up, &id)) {
+      res = FALSE;
+      break;
+    } else if (demux->level_up > 0) {
+      demux->level_up--;
+      break;
+    }
+
+    switch (id) {
+      case GST_MATROSKA_ID_CONTENTENCODING:{
+        GstMatroskaTrackEncoding enc = { 0, };
+
+        if (!gst_ebml_read_master (ebml, &id)) {
+          res = FALSE;
+          break;
+        }
+
+        while (res) {
+          if (!gst_ebml_peek_id (ebml, &demux->level_up, &id)) {
+            res = FALSE;
+            break;
+          } else if (demux->level_up > 0) {
+            demux->level_up--;
+            break;
+          }
+
+          switch (id) {
+            case GST_MATROSKA_ID_CONTENTENCODINGORDER:{
+              guint64 num;
+
+              if (!gst_ebml_read_uint (ebml, &id, &num)) {
+                res = FALSE;
+                break;
+              }
+              enc.order = num;
+              break;
+            }
+            case GST_MATROSKA_ID_CONTENTENCODINGSCOPE:{
+              guint64 num;
+
+              if (!gst_ebml_read_uint (ebml, &id, &num)) {
+                res = FALSE;
+                break;
+              }
+              if (num > 7)
+                GST_WARNING ("Unknown scope value in contents encoding.");
+              else
+                enc.scope = num;
+              break;
+            }
+            case GST_MATROSKA_ID_CONTENTENCODINGTYPE:{
+              guint64 num;
+
+              if (!gst_ebml_read_uint (ebml, &id, &num)) {
+                res = FALSE;
+                break;
+              }
+              if (num > 1)
+                GST_WARNING ("Unknown type value in contents encoding.");
+              else
+                enc.type = num;
+              break;
+            }
+            case GST_MATROSKA_ID_CONTENTCOMPRESSION:{
+
+              if (!gst_ebml_read_master (ebml, &id)) {
+                res = FALSE;
+                break;
+              }
+
+              while (res) {
+
+                if (!gst_ebml_peek_id (ebml, &demux->level_up, &id)) {
+                  res = FALSE;
+                  break;
+                } else if (demux->level_up > 0) {
+                  demux->level_up--;
+                  break;
+                }
+
+                switch (id) {
+                  case GST_MATROSKA_ID_CONTENTCOMPALGO:{
+                    guint64 num;
+
+                    if (!gst_ebml_read_uint (ebml, &id, &num)) {
+                      res = FALSE;
+                      break;
+                    }
+                    if (num > 3)
+                      GST_WARNING ("Unknown scope value in encoding compalgo.");
+                    else
+                      enc.comp_algo = num;
+                    break;
+                  }
+                  case GST_MATROSKA_ID_CONTENTCOMPSETTINGS:{
+                    guint8 *data;
+                    guint64 size;
+
+
+                    if (!gst_ebml_read_binary (ebml, &id, &data, &size)) {
+                      res = FALSE;
+                      break;
+                    }
+                    enc.comp_settings = data;
+                    enc.comp_settings_length = size;
+                    break;
+                  }
+                  default:
+                    GST_WARNING ("Unknown track compression header entry 0x%x"
+                        " - ignoring", id);
+                    if (!gst_ebml_read_skip (ebml))
+                      res = FALSE;
+                    break;
+                }
+
+                if (demux->level_up) {
+                  demux->level_up--;
+                  break;
+                }
+              }
+              break;
+            }
+
+            case GST_MATROSKA_ID_CONTENTENCRYPTION:
+              GST_WARNING ("Encrypted tracks not yet supported");
+              /* pass-through */
+            default:
+              GST_WARNING
+                  ("Unknown track encoding header entry 0x%x - ignoring", id);
+              if (!gst_ebml_read_skip (ebml))
+                res = FALSE;
+              break;
+          }
+
+          if (demux->level_up) {
+            demux->level_up--;
+            break;
+          }
+        }
+
+        g_array_append_val (context->encodings, enc);
+        break;
+      }
+
+      default:
+        GST_WARNING ("Unknown track encodings header entry 0x%x - ignoring",
+            id);
+        if (!gst_ebml_read_skip (ebml))
+          res = FALSE;
+        break;
+    }
+
+    if (demux->level_up) {
+      demux->level_up--;
+      break;
+    }
+  }
+
+  return res;
+}
+
+static gboolean
 gst_matroska_demux_add_stream (GstMatroskaDemux * demux)
 {
   GstElementClass *klass = GST_ELEMENT_GET_CLASS (demux);
@@ -795,6 +995,13 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux)
         break;
       }
 
+      case GST_MATROSKA_ID_CONTENTENCODINGS:{
+        if (!gst_matroska_demux_read_track_encodings (ebml, demux, context)) {
+          res = FALSE;
+        }
+        break;
+      }
+
       default:
         GST_WARNING ("Unknown track header entry 0x%x - ignoring", id);
         /* pass-through */
@@ -823,12 +1030,7 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux)
     demux->num_streams--;
     demux->src[demux->num_streams] = NULL;
     if (context) {
-      g_free (context->codec_id);
-      g_free (context->codec_name);
-      g_free (context->name);
-      g_free (context->language);
-      g_free (context->codec_priv);
-      g_free (context);
+      gst_matroska_track_free (context);
     }
 
     return res;
@@ -2269,6 +2471,83 @@ gst_matroska_demux_check_subtitle_buffer (GstMatroskaDemux * demux,
   return newbuf;
 }
 
+static GstBuffer *
+gst_matroska_decode_buffer (GstMatroskaTrackContext * context, GstBuffer * buf)
+{
+  gint i;
+  guint8 *new_data = NULL;
+  guint new_size = 0;
+  GstBuffer *res;
+
+  g_assert (context->encodings != NULL);
+
+  for (i = 0; i < context->encodings->len; i++) {
+    GstMatroskaTrackEncoding *enc;
+
+    enc = &g_array_index (context->encodings, GstMatroskaTrackEncoding, i);
+
+    /* FIXME: use enc->scope ? */
+
+    if (enc->comp_algo == 0) {
+#ifdef HAVE_ZLIB
+      /* zlib encoded track */
+      z_stream zstream;
+      guint orig_size;
+      int result;
+
+      orig_size = GST_BUFFER_SIZE (buf);
+      zstream.zalloc = (alloc_func) 0;
+      zstream.zfree = (free_func) 0;
+      zstream.opaque = (voidpf) 0;
+      if (inflateInit (&zstream) != Z_OK) {
+        GST_WARNING ("zlib initialization failed.\n");
+        return buf;
+      }
+      zstream.next_in = (Bytef *) GST_BUFFER_DATA (buf);
+      zstream.avail_in = orig_size;
+      new_size = orig_size;
+      new_data = g_malloc (new_size);
+      zstream.avail_out = new_size;
+      do {
+        new_size += 4000;
+        new_data = g_realloc (new_data, new_size);
+        zstream.next_out = (Bytef *) (new_data + zstream.total_out);
+        result = inflate (&zstream, Z_NO_FLUSH);
+        if (result != Z_OK && result != Z_STREAM_END) {
+          GST_WARNING ("zlib decompression failed.\n");
+          g_free (new_data);
+          inflateEnd (&zstream);
+          return buf;
+        }
+        zstream.avail_out += 4000;
+      } while (zstream.avail_out == 4000 &&
+          zstream.avail_in != 0 && result != Z_STREAM_END);
+
+      new_size = zstream.total_out;
+      inflateEnd (&zstream);
+#else
+      GST_WARNING ("GZIP encoded tracks not supported.");
+      return buf;
+#endif
+    } else if (enc->comp_algo == 1) {
+      GST_WARNING ("BZIP encoded tracks not supported.");
+      return buf;
+    } else if (enc->comp_algo == 2) {
+      GST_WARNING ("LZO encoded tracks not supported.");
+      return buf;
+    }
+  }
+
+  res = gst_buffer_new ();
+  GST_BUFFER_MALLOCDATA (res) = (guint8 *) new_data;
+  GST_BUFFER_DATA (res) = (guint8 *) new_data;
+  GST_BUFFER_SIZE (res) = new_size;
+  gst_buffer_stamp (res, buf);
+
+  gst_buffer_unref (buf);
+  return res;
+}
+
 static gboolean
 gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
     guint64 cluster_time, gboolean is_simpleblock)
@@ -2286,6 +2565,7 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
   gint64 time = 0;
   gint64 lace_time = 0;
   gint flags = 0;
+  gint64 referenceblock = 0;
 
   while (!got_error) {
     if (!is_simpleblock) {
@@ -2456,15 +2736,7 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
       }
 
       case GST_MATROSKA_ID_REFERENCEBLOCK:{
-        /* FIXME: implement support for ReferenceBlock
-           gint64 num;
-           if (!gst_ebml_read_sint (ebml, &id, &num)) {
-           res = FALSE;
-           break;
-           }
-           GST_WARNING ("FIXME: implement support for ReferenceBlock");
-         */
-        if (!gst_ebml_read_skip (ebml))
+        if (!gst_ebml_read_sint (ebml, &id, &referenceblock))
           got_error = TRUE;
         break;
       }
@@ -2497,6 +2769,13 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
     lace_time = GST_CLOCK_TIME_NONE;
   }
 
+  if (referenceblock && readblock && demux->src[stream_num]->set_discont) {
+    /* When doing seeks or such, we need to restart on key frames or
+       decoders might choke. */
+    readblock = FALSE;
+    gst_buffer_unref (buf);
+  }
+
   if (!got_error && readblock) {
     guint64 duration = 0;
 
@@ -2518,6 +2797,9 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
       sub = gst_buffer_create_sub (buf,
           GST_BUFFER_SIZE (buf) - size, lace_size[n]);
 
+      if (stream->encodings != NULL && stream->encodings->len > 0)
+        sub = gst_matroska_decode_buffer (stream, sub);
+
       GST_BUFFER_TIMESTAMP (sub) = lace_time;
       if (lace_time != GST_CLOCK_TIME_NONE)
         demux->pos = lace_time;
@@ -3609,6 +3891,9 @@ gst_matroska_demux_subtitle_caps (GstMatroskaTrackSubtitleContext *
   } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_SUBTITLE_USF)) {
     caps = gst_caps_new_simple ("application/x-usf", NULL);
     subtitlecontext->check_utf8 = TRUE;
+  } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_SUBTITLE_VOBSUB)) {
+    caps = gst_caps_new_simple ("video/x-dvd-subpicture", NULL);
+    subtitlecontext->check_utf8 = FALSE;
   } else {
     GST_DEBUG ("Unknown subtitle stream: codec_id='%s'", codec_id);
     caps = gst_caps_new_simple ("application/x-subtitle-unknown", NULL);
index 0c05974..5a5e572 100644 (file)
@@ -71,6 +71,7 @@
 #define GST_MATROSKA_ID_TRACKMINCACHE 0x6DE7
 #define GST_MATROSKA_ID_TRACKMAXCACHE 0x6DF8
 #define GST_MATROSKA_ID_TRACKDEFAULTDURATION 0x23E383
+#define GST_MATROSKA_ID_CONTENTENCODINGS 0x6D80
 
 /* IDs in the trackvideo master */
 #define GST_MATROSKA_ID_VIDEOFRAMERATE 0x2383E3
 #define GST_MATROSKA_ID_BLOCK      0xA1
 #define GST_MATROSKA_ID_BLOCKDURATION 0x9B
 
+/* IDs in the contentencodings master */
+#define GST_MATROSKA_ID_CONTENTENCODING 0x6240
+
+/* IDS IN THE CONTENTENCODING MASTER */
+#define GST_MATROSKA_ID_CONTENTENCODINGORDER 0X5031
+#define GST_MATROSKA_ID_CONTENTENCODINGSCOPE 0X5032
+#define GST_MATROSKA_ID_CONTENTENCODINGTYPE  0X5033
+#define GST_MATROSKA_ID_CONTENTCOMPRESSION   0X5034
+#define GST_MATROSKA_ID_CONTENTENCRYPTION    0X5035
+
+/* IDS IN THE CONTENTCOMPRESSION MASTER */
+#define GST_MATROSKA_ID_CONTENTCOMPALGO      0X4254
+#define GST_MATROSKA_ID_CONTENTCOMPSETTINGS  0X4255
+
+
 /*
  * Matroska Codec IDs. Strings.
  */
 #define GST_MATROSKA_CODEC_ID_SUBTITLE_SSA       "S_TEXT/SSA"
 #define GST_MATROSKA_CODEC_ID_SUBTITLE_ASS       "S_TEXT/ASS" 
 #define GST_MATROSKA_CODEC_ID_SUBTITLE_USF       "S_TEXT/USF"
+#define GST_MATROSKA_CODEC_ID_SUBTITLE_VOBSUB    "S_VOBSUB"
 
 /*
  * Matrodka tags. Strings.
@@ -266,6 +283,10 @@ typedef struct _GstMatroskaTrackContext {
 
   /* Tags to send after newsegment event */
   GstTagList   *pending_tags;
+
+  /* A GArray of GstMatroskaTrackEncoding structures which contain the
+   * encoding (compression/encryption) settings for this track, if any */
+  GArray       *encodings;
 } GstMatroskaTrackContext;
 
 typedef struct _GstMatroskaTrackVideoContext {
@@ -316,6 +337,15 @@ typedef struct _Wavpack4Header {
   guint32 crc;           /* crc for actual decoded data                    */
 } Wavpack4Header;
 
+typedef struct _GstMatroskaTrackEncoding {
+  guint   order;
+  guint   scope     : 3;
+  guint   type      : 1;
+  guint   comp_algo : 2;
+  guint8 *comp_settings;
+  guint   comp_settings_length;
+} GstMatroskaTrackEncoding;
+
 gboolean gst_matroska_track_init_video_context    (GstMatroskaTrackContext ** p_context);
 gboolean gst_matroska_track_init_audio_context    (GstMatroskaTrackContext ** p_context);
 gboolean gst_matroska_track_init_subtitle_context (GstMatroskaTrackContext ** p_context);