flacparse: Optionally check the overall frame checksums too before accepting a frame...
authorSebastian Dröge <sebastian.droege@collabora.co.uk>
Fri, 26 Mar 2010 17:58:35 +0000 (18:58 +0100)
committerTim-Philipp Müller <tim.muller@collabora.co.uk>
Fri, 8 Apr 2011 17:07:06 +0000 (18:07 +0100)
This is optional because it's a quite expensive operation and it's very
unlikely that a non-frame is detected as frame after the header CRC check
and checking all bits for valid values. The overall frame checksums are
mainly useful to detect inconsistencies in the encoded payload.

gst/audioparsers/gstflacparse.c
gst/audioparsers/gstflacparse.h

index ba9d7c4..0670fc0 100644 (file)
@@ -114,6 +114,63 @@ gst_flac_calculate_crc8 (const guint8 * data, guint length)
   return crc;
 }
 
+/* CRC-16, poly = x^16 + x^15 + x^2 + x^0, init = 0 */
+static const guint16 crc16_table[256] = {
+  0x0000, 0x8005, 0x800f, 0x000a, 0x801b, 0x001e, 0x0014, 0x8011,
+  0x8033, 0x0036, 0x003c, 0x8039, 0x0028, 0x802d, 0x8027, 0x0022,
+  0x8063, 0x0066, 0x006c, 0x8069, 0x0078, 0x807d, 0x8077, 0x0072,
+  0x0050, 0x8055, 0x805f, 0x005a, 0x804b, 0x004e, 0x0044, 0x8041,
+  0x80c3, 0x00c6, 0x00cc, 0x80c9, 0x00d8, 0x80dd, 0x80d7, 0x00d2,
+  0x00f0, 0x80f5, 0x80ff, 0x00fa, 0x80eb, 0x00ee, 0x00e4, 0x80e1,
+  0x00a0, 0x80a5, 0x80af, 0x00aa, 0x80bb, 0x00be, 0x00b4, 0x80b1,
+  0x8093, 0x0096, 0x009c, 0x8099, 0x0088, 0x808d, 0x8087, 0x0082,
+  0x8183, 0x0186, 0x018c, 0x8189, 0x0198, 0x819d, 0x8197, 0x0192,
+  0x01b0, 0x81b5, 0x81bf, 0x01ba, 0x81ab, 0x01ae, 0x01a4, 0x81a1,
+  0x01e0, 0x81e5, 0x81ef, 0x01ea, 0x81fb, 0x01fe, 0x01f4, 0x81f1,
+  0x81d3, 0x01d6, 0x01dc, 0x81d9, 0x01c8, 0x81cd, 0x81c7, 0x01c2,
+  0x0140, 0x8145, 0x814f, 0x014a, 0x815b, 0x015e, 0x0154, 0x8151,
+  0x8173, 0x0176, 0x017c, 0x8179, 0x0168, 0x816d, 0x8167, 0x0162,
+  0x8123, 0x0126, 0x012c, 0x8129, 0x0138, 0x813d, 0x8137, 0x0132,
+  0x0110, 0x8115, 0x811f, 0x011a, 0x810b, 0x010e, 0x0104, 0x8101,
+  0x8303, 0x0306, 0x030c, 0x8309, 0x0318, 0x831d, 0x8317, 0x0312,
+  0x0330, 0x8335, 0x833f, 0x033a, 0x832b, 0x032e, 0x0324, 0x8321,
+  0x0360, 0x8365, 0x836f, 0x036a, 0x837b, 0x037e, 0x0374, 0x8371,
+  0x8353, 0x0356, 0x035c, 0x8359, 0x0348, 0x834d, 0x8347, 0x0342,
+  0x03c0, 0x83c5, 0x83cf, 0x03ca, 0x83db, 0x03de, 0x03d4, 0x83d1,
+  0x83f3, 0x03f6, 0x03fc, 0x83f9, 0x03e8, 0x83ed, 0x83e7, 0x03e2,
+  0x83a3, 0x03a6, 0x03ac, 0x83a9, 0x03b8, 0x83bd, 0x83b7, 0x03b2,
+  0x0390, 0x8395, 0x839f, 0x039a, 0x838b, 0x038e, 0x0384, 0x8381,
+  0x0280, 0x8285, 0x828f, 0x028a, 0x829b, 0x029e, 0x0294, 0x8291,
+  0x82b3, 0x02b6, 0x02bc, 0x82b9, 0x02a8, 0x82ad, 0x82a7, 0x02a2,
+  0x82e3, 0x02e6, 0x02ec, 0x82e9, 0x02f8, 0x82fd, 0x82f7, 0x02f2,
+  0x02d0, 0x82d5, 0x82df, 0x02da, 0x82cb, 0x02ce, 0x02c4, 0x82c1,
+  0x8243, 0x0246, 0x024c, 0x8249, 0x0258, 0x825d, 0x8257, 0x0252,
+  0x0270, 0x8275, 0x827f, 0x027a, 0x826b, 0x026e, 0x0264, 0x8261,
+  0x0220, 0x8225, 0x822f, 0x022a, 0x823b, 0x023e, 0x0234, 0x8231,
+  0x8213, 0x0216, 0x021c, 0x8219, 0x0208, 0x820d, 0x8207, 0x0202
+};
+
+static guint16
+gst_flac_calculate_crc16 (const guint8 * data, guint length)
+{
+  guint16 crc = 0;
+
+  while (length--) {
+    crc = ((crc << 8) ^ crc16_table[(crc >> 8) ^ *data]) & 0xffff;
+    data++;
+  }
+
+  return crc;
+}
+
+enum
+{
+  PROP_0,
+  PROP_CHECK_FRAME_CHECKSUMS
+};
+
+#define DEFAULT_CHECK_FRAME_CHECKSUMS FALSE
+
 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
     GST_PAD_SRC,
     GST_PAD_ALWAYS,
@@ -128,6 +185,10 @@ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
     );
 
 static void gst_flac_parse_finalize (GObject * object);
+static void gst_flac_parse_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void gst_flac_parse_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
 
 static gboolean gst_flac_parse_start (GstBaseParse * parse);
 static gboolean gst_flac_parse_stop (GstBaseParse * parse);
@@ -167,6 +228,14 @@ gst_flac_parse_class_init (GstFlacParseClass * klass)
   GstBaseParseClass *baseparse_class = GST_BASE_PARSE_CLASS (klass);
 
   gobject_class->finalize = gst_flac_parse_finalize;
+  gobject_class->set_property = gst_flac_parse_set_property;
+  gobject_class->get_property = gst_flac_parse_get_property;
+
+  g_object_class_install_property (gobject_class, PROP_CHECK_FRAME_CHECKSUMS,
+      g_param_spec_boolean ("check-frame-checksums", "Check Frame Checksums",
+          "Check the overall checksums of every frame",
+          DEFAULT_CHECK_FRAME_CHECKSUMS,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   baseparse_class->start = GST_DEBUG_FUNCPTR (gst_flac_parse_start);
   baseparse_class->stop = GST_DEBUG_FUNCPTR (gst_flac_parse_stop);
@@ -180,6 +249,39 @@ gst_flac_parse_class_init (GstFlacParseClass * klass)
 static void
 gst_flac_parse_init (GstFlacParse * flacparse, GstFlacParseClass * klass)
 {
+  flacparse->check_frame_checksums = DEFAULT_CHECK_FRAME_CHECKSUMS;
+}
+
+static void
+gst_flac_parse_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstFlacParse *flacparse = GST_FLAC_PARSE (object);
+
+  switch (prop_id) {
+    case PROP_CHECK_FRAME_CHECKSUMS:
+      flacparse->check_frame_checksums = g_value_get_boolean (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_flac_parse_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstFlacParse *flacparse = GST_FLAC_PARSE (object);
+
+  switch (prop_id) {
+    case PROP_CHECK_FRAME_CHECKSUMS:
+      g_value_set_boolean (value, flacparse->check_frame_checksums);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
 }
 
 static void
@@ -659,9 +761,22 @@ gst_flac_parse_get_frame_size (GstFlacParse * flacparse, GstBuffer * buffer,
   /* zero padding to byte alignment */
   gst_bit_reader_skip_to_byte (&reader);
 
-  /* Skip crc-16 for the complete frame */
-  if (!gst_bit_reader_skip (&reader, 16))
-    goto need_more_data;
+  if (flacparse->check_frame_checksums) {
+    guint16 actual_crc16, expected_crc16;
+
+    if (!gst_bit_reader_get_bits_uint16 (&reader, &expected_crc16, 16))
+      goto need_more_data;
+
+    actual_crc16 =
+        gst_flac_calculate_crc16 (GST_BUFFER_DATA (buffer),
+        (gst_bit_reader_get_pos (&reader) / 8) - 2);
+    if (actual_crc16 != expected_crc16)
+      goto error;
+  } else {
+    /* Skip crc-16 for the complete frame */
+    if (!gst_bit_reader_skip (&reader, 16))
+      goto need_more_data;
+  }
 
   *framesize_ret = gst_bit_reader_get_pos (&reader) / 8;
 
index af57ba7..4730e23 100644 (file)
@@ -54,6 +54,9 @@ typedef struct {
 struct _GstFlacParse {
   GstBaseParse parent;
 
+  /* Properties */
+  gboolean check_frame_checksums;
+
   GstFlacParseState state;
 
   gint64 upstream_length;