examples: add small jpeg codecparser test
authorTim-Philipp Müller <tim@centricular.com>
Sun, 21 Jun 2015 10:20:57 +0000 (11:20 +0100)
committerTim-Philipp Müller <tim@centricular.com>
Sun, 21 Jun 2015 10:34:39 +0000 (11:34 +0100)
https://bugzilla.gnome.org/show_bug.cgi?id=673925

.gitignore
configure.ac
tests/examples/Makefile.am
tests/examples/codecparsers/Makefile.am [new file with mode: 0644]
tests/examples/codecparsers/parse-jpeg.c [new file with mode: 0644]

index aac56a8..90b0537 100644 (file)
@@ -52,6 +52,7 @@ gst*orc.h
 /ext/gsettings/org.freedesktop.gstreamer-0.10.default-elements.gschema.xml
 
 /tests/check/orc
+/tests/examples/codecparsers/parse-jpeg
 /tests/examples/shapewipe/shapewipe-example
 /tests/examples/jack/jack_client
 /tests/examples/opencv/gstmotioncells_dynamic_test
index 3de8d68..501cdef 100644 (file)
@@ -3273,6 +3273,7 @@ tests/files/Makefile
 tests/examples/Makefile
 tests/examples/avsamplesink/Makefile
 tests/examples/camerabin2/Makefile
+tests/examples/codecparsers/Makefile
 tests/examples/directfb/Makefile
 tests/examples/gl/Makefile
 tests/examples/gl/cocoa/Makefile
index 009ff73..c26d3d9 100644 (file)
@@ -46,8 +46,8 @@ playout_SOURCES = playout.c
 playout_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS)
 playout_LDADD = $(GST_PLUGINS_BASE_LIBS) -lgstvideo-$(GST_API_VERSION) $(GST_LIBS)
 
-SUBDIRS= mpegts $(DIRECTFB_DIR) $(GTK_EXAMPLES) $(OPENCV_EXAMPLES) $(GL_DIR) \
-       $(GTK3_DIR) $(AVSAMPLE_DIR)
-DIST_SUBDIRS= mpegts camerabin2 directfb mxf opencv uvch264 gl gtk avsamplesink
+SUBDIRS= codecparsers mpegts $(DIRECTFB_DIR) $(GTK_EXAMPLES) $(OPENCV_EXAMPLES) \
+        $(GL_DIR) $(GTK3_DIR) $(AVSAMPLE_DIR)
+DIST_SUBDIRS= codecparsers mpegts camerabin2 directfb mxf opencv uvch264 gl gtk avsamplesink
 
 include $(top_srcdir)/common/parallel-subdirs.mak
diff --git a/tests/examples/codecparsers/Makefile.am b/tests/examples/codecparsers/Makefile.am
new file mode 100644 (file)
index 0000000..1a3b87c
--- /dev/null
@@ -0,0 +1,7 @@
+noinst_PROGRAMS = parse-jpeg
+
+parse_jpeg_SOURCES = parse-jpeg.c
+parse_jpeg_CFLAGS = $(GST_PLUGINS_BAD_CFLAGS) $(GST_CFLAGS)
+parse_jpeg_LDFLAGS = $(GST_LIBS)
+parse_jpeg_LDADD = \
+       $(top_builddir)/gst-libs/gst/codecparsers/libgstcodecparsers-$(GST_API_VERSION).la
diff --git a/tests/examples/codecparsers/parse-jpeg.c b/tests/examples/codecparsers/parse-jpeg.c
new file mode 100644 (file)
index 0000000..fe42999
--- /dev/null
@@ -0,0 +1,307 @@
+/* GStreamer JPEG parser test utility
+ * Copyright (C) 2015 Tim-Philipp Müller <tim@centricular.com>
+ *
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <gst/codecparsers/gstjpegparser.h>
+
+#include <stdlib.h>
+
+static GstBuffer *app_segments[16];     /* NULL */
+
+static const gchar *
+get_marker_name (guint8 marker)
+{
+  switch (marker) {
+    case GST_JPEG_MARKER_SOF0:
+      return "SOF (Baseline)";
+    case GST_JPEG_MARKER_SOF1:
+      return "SOF (Extended Sequential, Huffman)";
+    case GST_JPEG_MARKER_SOF2:
+      return "SOF (Extended Progressive, Huffman)";
+    case GST_JPEG_MARKER_SOF3:
+      return "SOF (Lossless, Huffman)";
+    case GST_JPEG_MARKER_SOF5:
+      return "SOF (Differential Sequential, Huffman)";
+    case GST_JPEG_MARKER_SOF6:
+      return "SOF (Differential Progressive, Huffman)";
+    case GST_JPEG_MARKER_SOF7:
+      return "SOF (Differential Lossless, Huffman)";
+    case GST_JPEG_MARKER_SOF9:
+      return "SOF (Extended Sequential, Arithmetic)";
+    case GST_JPEG_MARKER_SOF10:
+      return "SOF (Progressive, Arithmetic)";
+    case GST_JPEG_MARKER_SOF11:
+      return "SOF (Lossless, Arithmetic)";
+    case GST_JPEG_MARKER_SOF13:
+      return "SOF (Differential Sequential, Arithmetic)";
+    case GST_JPEG_MARKER_SOF14:
+      return "SOF (Differential Progressive, Arithmetic)";
+    case GST_JPEG_MARKER_SOF15:
+      return "SOF (Differential Lossless, Arithmetic)";
+    case GST_JPEG_MARKER_DHT:
+      return "DHT";
+    case GST_JPEG_MARKER_DAC:
+      return "DAC";
+    case GST_JPEG_MARKER_SOI:
+      return "SOI";
+    case GST_JPEG_MARKER_EOI:
+      return "EOI";
+    case GST_JPEG_MARKER_SOS:
+      return "SOS";
+    case GST_JPEG_MARKER_DQT:
+      return "DQT";
+    case GST_JPEG_MARKER_DNL:
+      return "DNL";
+    case GST_JPEG_MARKER_DRI:
+      return "DRI";
+    case GST_JPEG_MARKER_APP0:
+      return "APP0";
+    case GST_JPEG_MARKER_APP1:
+      return "APP1";
+    case GST_JPEG_MARKER_APP2:
+      return "APP2";
+    case GST_JPEG_MARKER_APP3:
+      return "APP3";
+    case GST_JPEG_MARKER_APP4:
+      return "APP4";
+    case GST_JPEG_MARKER_APP5:
+      return "APP5";
+    case GST_JPEG_MARKER_APP6:
+      return "APP6";
+    case GST_JPEG_MARKER_APP7:
+      return "APP7";
+    case GST_JPEG_MARKER_APP8:
+      return "APP8";
+    case GST_JPEG_MARKER_APP9:
+      return "APP9";
+    case GST_JPEG_MARKER_APP10:
+      return "APP10";
+    case GST_JPEG_MARKER_APP11:
+      return "APP11";
+    case GST_JPEG_MARKER_APP12:
+      return "APP12";
+    case GST_JPEG_MARKER_APP13:
+      return "APP13";
+    case GST_JPEG_MARKER_APP14:
+      return "APP14";
+    case GST_JPEG_MARKER_APP15:
+      return "APP15";
+    case GST_JPEG_MARKER_COM:
+      return "COM";
+    default:
+      if (marker > GST_JPEG_MARKER_RST_MIN && marker < GST_JPEG_MARKER_RST_MAX)
+        return "RST";
+      break;
+  }
+  return "???";
+}
+
+static gboolean
+parse_jpeg_segment (GstJpegSegment * segment)
+{
+  switch (segment->marker) {
+    case GST_JPEG_MARKER_SOF0:
+    case GST_JPEG_MARKER_SOF1:
+    case GST_JPEG_MARKER_SOF2:
+    case GST_JPEG_MARKER_SOF3:
+    case GST_JPEG_MARKER_SOF9:
+    case GST_JPEG_MARKER_SOF10:
+    case GST_JPEG_MARKER_SOF11:{
+      GstJpegFrameHdr hdr;
+      int i;
+
+      if (!gst_jpeg_segment_parse_frame_header (segment, &hdr)) {
+        g_printerr ("Failed to parse frame header!\n");
+        return FALSE;
+      }
+
+      g_print ("\t\twidth x height   = %u x %u\n", hdr.width, hdr.height);
+      g_print ("\t\tsample precision = %u\n", hdr.sample_precision);
+      g_print ("\t\tnum components   = %u\n", hdr.num_components);
+      for (i = 0; i < hdr.num_components; ++i) {
+        g_print ("\t\t%d: id=%d, h=%d, v=%d, qts=%d\n", i,
+            hdr.components[i].identifier, hdr.components[i].horizontal_factor,
+            hdr.components[i].vertical_factor,
+            hdr.components[i].quant_table_selector);
+      }
+      break;
+    }
+    case GST_JPEG_MARKER_DHT:{
+      GstJpegHuffmanTables ht;
+
+      if (!gst_jpeg_segment_parse_huffman_table (segment, &ht)) {
+        g_printerr ("Failed to parse huffman table!\n");
+        return FALSE;
+      }
+      break;
+    }
+    case GST_JPEG_MARKER_DQT:{
+      GstJpegQuantTables qt;
+
+      if (!gst_jpeg_segment_parse_quantization_table (segment, &qt)) {
+        g_printerr ("Failed to parse quantization table!\n");
+        return FALSE;
+      }
+      break;
+    }
+    case GST_JPEG_MARKER_SOS:{
+      GstJpegScanHdr hdr;
+      int i;
+
+      if (!gst_jpeg_segment_parse_scan_header (segment, &hdr)) {
+        g_printerr ("Failed to parse scan header!\n");
+        return FALSE;
+      }
+
+      g_print ("\t\tnum components   = %u\n", hdr.num_components);
+      for (i = 0; i < hdr.num_components; ++i) {
+        g_print ("\t\t  %d: cs=%d, dcs=%d, acs=%d\n", i,
+            hdr.components[i].component_selector,
+            hdr.components[i].dc_selector, hdr.components[i].ac_selector);
+      }
+    }
+    case GST_JPEG_MARKER_COM:
+      /* gst_util_dump_mem (segment->data + segment->offset, segment->size); */
+      break;
+    default:
+      if (segment->marker >= GST_JPEG_MARKER_APP_MIN
+          && segment->marker <= GST_JPEG_MARKER_APP_MAX) {
+        guint n = segment->marker - GST_JPEG_MARKER_APP_MIN;
+
+        if (app_segments[n] == NULL)
+          app_segments[n] = gst_buffer_new ();
+
+        gst_buffer_append_memory (app_segments[n],
+            gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY,
+                (guint8 *) segment->data + segment->offset,
+                segment->size, 0, segment->size, NULL, NULL));
+      }
+      break;
+  }
+  return TRUE;
+}
+
+static void
+parse_jpeg (const guint8 * data, gsize size)
+{
+  GstJpegSegment segment;
+  guint offset = 0;
+
+  while (gst_jpeg_parse (&segment, data, size, offset)) {
+    if (segment.offset > offset + 2)
+      g_print ("  skipped %u bytes\n", (guint) segment.offset - offset - 2);
+
+    g_print ("%6d bytes at offset %-8u : %s\n", (gint) segment.size,
+        segment.offset, get_marker_name (segment.marker));
+
+    if (segment.marker == GST_JPEG_MARKER_EOI)
+      break;
+
+    if (offset + segment.size < size &&
+        parse_jpeg_segment (&segment) && segment.size >= 0)
+      offset = segment.offset + segment.size;
+    else
+      offset += 2;
+  };
+}
+
+static void
+process_file (const gchar * fn)
+{
+  GError *err = NULL;
+  gchar *data = NULL;
+  gsize size = 0;
+  guint i;
+
+  g_print ("===============================================================\n");
+  g_print (" %s\n", fn);
+  g_print ("===============================================================\n");
+
+  if (!g_file_get_contents (fn, &data, &size, &err)) {
+    g_error ("%s", err->message);
+    g_error_free (err);
+    return;
+  }
+
+  parse_jpeg ((const guint8 *) data, size);
+
+  for (i = 0; i < G_N_ELEMENTS (app_segments); ++i) {
+    if (app_segments[i] != NULL) {
+      GstMapInfo map = GST_MAP_INFO_INIT;
+
+      /* Could parse/extract tags here */
+      gst_buffer_map (app_segments[i], &map, GST_MAP_READ);
+      g_print ("\tAPP%-2u : %u bytes\n", i, (guint) map.size);
+      gst_util_dump_mem ((guchar *) map.data, MIN (map.size, 16));
+      gst_buffer_unmap (app_segments[i], &map);
+      gst_buffer_unref (app_segments[i]);
+      app_segments[i] = NULL;
+    }
+  }
+
+  g_free (data);
+}
+
+int
+main (int argc, gchar ** argv)
+{
+  gchar **filenames = NULL;
+  GOptionEntry options[] = {
+    {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL},
+    {NULL}
+  };
+  GOptionContext *ctx;
+  GError *err = NULL;
+  guint i, num;
+
+  gst_init (&argc, &argv);
+
+  if (argc == 1) {
+    g_printerr ("Usage: %s FILE.JPG [FILE2.JPG] [FILE..JPG]\n", argv[0]);
+    return -1;
+  }
+
+  ctx = g_option_context_new ("JPEG FILES");
+  g_option_context_add_main_entries (ctx, options, GETTEXT_PACKAGE);
+  g_option_context_add_group (ctx, gst_init_get_option_group ());
+  if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
+    g_print ("Error initializing: %s\n", GST_STR_NULL (err->message));
+    exit (1);
+  }
+  g_option_context_free (ctx);
+
+  if (filenames == NULL || *filenames == NULL) {
+    g_printerr ("Please provide one or more filenames.");
+    return 1;
+  }
+
+  num = g_strv_length (filenames);
+
+  for (i = 0; i < num; ++i) {
+    process_file (filenames[i]);
+  }
+
+  g_strfreev (filenames);
+
+  return 0;
+}