1 /* GStreamer JPEG parser test utility
2  * Copyright (C) 2015 Tim-Philipp Müller <tim@centricular.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #include <gst/gst.h>
25 #include <gst/codecparsers/gstjpegparser.h>
26 
27 #include <stdlib.h>
28 
29 static GstBuffer *app_segments[16];     /* NULL */
30 
31 static const gchar *
get_marker_name(guint8 marker)32 get_marker_name (guint8 marker)
33 {
34   switch (marker) {
35     case GST_JPEG_MARKER_SOF0:
36       return "SOF (Baseline)";
37     case GST_JPEG_MARKER_SOF1:
38       return "SOF (Extended Sequential, Huffman)";
39     case GST_JPEG_MARKER_SOF2:
40       return "SOF (Extended Progressive, Huffman)";
41     case GST_JPEG_MARKER_SOF3:
42       return "SOF (Lossless, Huffman)";
43     case GST_JPEG_MARKER_SOF5:
44       return "SOF (Differential Sequential, Huffman)";
45     case GST_JPEG_MARKER_SOF6:
46       return "SOF (Differential Progressive, Huffman)";
47     case GST_JPEG_MARKER_SOF7:
48       return "SOF (Differential Lossless, Huffman)";
49     case GST_JPEG_MARKER_SOF9:
50       return "SOF (Extended Sequential, Arithmetic)";
51     case GST_JPEG_MARKER_SOF10:
52       return "SOF (Progressive, Arithmetic)";
53     case GST_JPEG_MARKER_SOF11:
54       return "SOF (Lossless, Arithmetic)";
55     case GST_JPEG_MARKER_SOF13:
56       return "SOF (Differential Sequential, Arithmetic)";
57     case GST_JPEG_MARKER_SOF14:
58       return "SOF (Differential Progressive, Arithmetic)";
59     case GST_JPEG_MARKER_SOF15:
60       return "SOF (Differential Lossless, Arithmetic)";
61     case GST_JPEG_MARKER_DHT:
62       return "DHT";
63     case GST_JPEG_MARKER_DAC:
64       return "DAC";
65     case GST_JPEG_MARKER_SOI:
66       return "SOI";
67     case GST_JPEG_MARKER_EOI:
68       return "EOI";
69     case GST_JPEG_MARKER_SOS:
70       return "SOS";
71     case GST_JPEG_MARKER_DQT:
72       return "DQT";
73     case GST_JPEG_MARKER_DNL:
74       return "DNL";
75     case GST_JPEG_MARKER_DRI:
76       return "DRI";
77     case GST_JPEG_MARKER_APP0:
78       return "APP0";
79     case GST_JPEG_MARKER_APP1:
80       return "APP1";
81     case GST_JPEG_MARKER_APP2:
82       return "APP2";
83     case GST_JPEG_MARKER_APP3:
84       return "APP3";
85     case GST_JPEG_MARKER_APP4:
86       return "APP4";
87     case GST_JPEG_MARKER_APP5:
88       return "APP5";
89     case GST_JPEG_MARKER_APP6:
90       return "APP6";
91     case GST_JPEG_MARKER_APP7:
92       return "APP7";
93     case GST_JPEG_MARKER_APP8:
94       return "APP8";
95     case GST_JPEG_MARKER_APP9:
96       return "APP9";
97     case GST_JPEG_MARKER_APP10:
98       return "APP10";
99     case GST_JPEG_MARKER_APP11:
100       return "APP11";
101     case GST_JPEG_MARKER_APP12:
102       return "APP12";
103     case GST_JPEG_MARKER_APP13:
104       return "APP13";
105     case GST_JPEG_MARKER_APP14:
106       return "APP14";
107     case GST_JPEG_MARKER_APP15:
108       return "APP15";
109     case GST_JPEG_MARKER_COM:
110       return "COM";
111     default:
112       if (marker > GST_JPEG_MARKER_RST_MIN && marker < GST_JPEG_MARKER_RST_MAX)
113         return "RST";
114       break;
115   }
116   return "???";
117 }
118 
119 static gboolean
parse_jpeg_segment(GstJpegSegment * segment)120 parse_jpeg_segment (GstJpegSegment * segment)
121 {
122   switch (segment->marker) {
123     case GST_JPEG_MARKER_SOF0:
124     case GST_JPEG_MARKER_SOF1:
125     case GST_JPEG_MARKER_SOF2:
126     case GST_JPEG_MARKER_SOF3:
127     case GST_JPEG_MARKER_SOF9:
128     case GST_JPEG_MARKER_SOF10:
129     case GST_JPEG_MARKER_SOF11:{
130       GstJpegFrameHdr hdr;
131       int i;
132 
133       if (!gst_jpeg_segment_parse_frame_header (segment, &hdr)) {
134         g_printerr ("Failed to parse frame header!\n");
135         return FALSE;
136       }
137 
138       g_print ("\t\twidth x height   = %u x %u\n", hdr.width, hdr.height);
139       g_print ("\t\tsample precision = %u\n", hdr.sample_precision);
140       g_print ("\t\tnum components   = %u\n", hdr.num_components);
141       for (i = 0; i < hdr.num_components; ++i) {
142         g_print ("\t\t%d: id=%d, h=%d, v=%d, qts=%d\n", i,
143             hdr.components[i].identifier, hdr.components[i].horizontal_factor,
144             hdr.components[i].vertical_factor,
145             hdr.components[i].quant_table_selector);
146       }
147       break;
148     }
149     case GST_JPEG_MARKER_DHT:{
150       GstJpegHuffmanTables ht;
151 
152       if (!gst_jpeg_segment_parse_huffman_table (segment, &ht)) {
153         g_printerr ("Failed to parse huffman table!\n");
154         return FALSE;
155       }
156       break;
157     }
158     case GST_JPEG_MARKER_DQT:{
159       GstJpegQuantTables qt;
160 
161       if (!gst_jpeg_segment_parse_quantization_table (segment, &qt)) {
162         g_printerr ("Failed to parse quantization table!\n");
163         return FALSE;
164       }
165       break;
166     }
167     case GST_JPEG_MARKER_SOS:{
168       GstJpegScanHdr hdr;
169       int i;
170 
171       if (!gst_jpeg_segment_parse_scan_header (segment, &hdr)) {
172         g_printerr ("Failed to parse scan header!\n");
173         return FALSE;
174       }
175 
176       g_print ("\t\tnum components   = %u\n", hdr.num_components);
177       for (i = 0; i < hdr.num_components; ++i) {
178         g_print ("\t\t  %d: cs=%d, dcs=%d, acs=%d\n", i,
179             hdr.components[i].component_selector,
180             hdr.components[i].dc_selector, hdr.components[i].ac_selector);
181       }
182     }
183     case GST_JPEG_MARKER_COM:
184       /* gst_util_dump_mem (segment->data + segment->offset, segment->size); */
185       break;
186     default:
187       if (segment->marker >= GST_JPEG_MARKER_APP_MIN
188           && segment->marker <= GST_JPEG_MARKER_APP_MAX) {
189         guint n = segment->marker - GST_JPEG_MARKER_APP_MIN;
190 
191         if (app_segments[n] == NULL)
192           app_segments[n] = gst_buffer_new ();
193 
194         gst_buffer_append_memory (app_segments[n],
195             gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY,
196                 (guint8 *) segment->data + segment->offset,
197                 segment->size, 0, segment->size, NULL, NULL));
198       }
199       break;
200   }
201   return TRUE;
202 }
203 
204 static void
parse_jpeg(const guint8 * data,gsize size)205 parse_jpeg (const guint8 * data, gsize size)
206 {
207   GstJpegSegment segment;
208   guint offset = 0;
209 
210   while (gst_jpeg_parse (&segment, data, size, offset)) {
211     if (segment.offset > offset + 2)
212       g_print ("  skipped %u bytes\n", (guint) segment.offset - offset - 2);
213 
214     g_print ("%6d bytes at offset %-8u : %s\n", (gint) segment.size,
215         segment.offset, get_marker_name (segment.marker));
216 
217     if (segment.marker == GST_JPEG_MARKER_EOI)
218       break;
219 
220     if (offset + segment.size < size &&
221         parse_jpeg_segment (&segment) && segment.size >= 0)
222       offset = segment.offset + segment.size;
223     else
224       offset += 2;
225   };
226 }
227 
228 static void
process_file(const gchar * fn)229 process_file (const gchar * fn)
230 {
231   GError *err = NULL;
232   gchar *data = NULL;
233   gsize size = 0;
234   guint i;
235 
236   g_print ("===============================================================\n");
237   g_print (" %s\n", fn);
238   g_print ("===============================================================\n");
239 
240   if (!g_file_get_contents (fn, &data, &size, &err)) {
241     g_error ("%s", err->message);
242     g_clear_error (&err);
243     return;
244   }
245 
246   parse_jpeg ((const guint8 *) data, size);
247 
248   for (i = 0; i < G_N_ELEMENTS (app_segments); ++i) {
249     if (app_segments[i] != NULL) {
250       GstMapInfo map = GST_MAP_INFO_INIT;
251 
252       /* Could parse/extract tags here */
253       gst_buffer_map (app_segments[i], &map, GST_MAP_READ);
254       g_print ("\tAPP%-2u : %u bytes\n", i, (guint) map.size);
255       gst_util_dump_mem ((guchar *) map.data, MIN (map.size, 16));
256       gst_buffer_unmap (app_segments[i], &map);
257       gst_buffer_unref (app_segments[i]);
258       app_segments[i] = NULL;
259     }
260   }
261 
262   g_free (data);
263 }
264 
265 int
main(int argc,gchar ** argv)266 main (int argc, gchar ** argv)
267 {
268   gchar **filenames = NULL;
269   GOptionEntry options[] = {
270     {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL},
271     {NULL}
272   };
273   GOptionContext *ctx;
274   GError *err = NULL;
275   guint i, num;
276 
277   gst_init (&argc, &argv);
278 
279   if (argc == 1) {
280     g_printerr ("Usage: %s FILE.JPG [FILE2.JPG] [FILE..JPG]\n", argv[0]);
281     return -1;
282   }
283 
284   ctx = g_option_context_new ("JPEG FILES");
285   g_option_context_add_main_entries (ctx, options, GETTEXT_PACKAGE);
286   g_option_context_add_group (ctx, gst_init_get_option_group ());
287   if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
288     g_print ("Error initializing: %s\n", GST_STR_NULL (err->message));
289     g_option_context_free (ctx);
290     g_clear_error (&err);
291     exit (1);
292   }
293   g_option_context_free (ctx);
294 
295   if (filenames == NULL || *filenames == NULL) {
296     g_printerr ("Please provide one or more filenames.");
297     return 1;
298   }
299 
300   num = g_strv_length (filenames);
301 
302   for (i = 0; i < num; ++i) {
303     process_file (filenames[i]);
304   }
305 
306   g_strfreev (filenames);
307 
308   return 0;
309 }
310