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