1 /*
2 * import_im.c
3 *
4 * Copyright (C) Thomas Oestreich - June 2001
5 * port to MagickWand API:
6 * Copyright (C) Francesco Romani - July 2007
7 *
8 * This file is part of transcode, a video stream processing tool
9 *
10 * transcode is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2, or (at your option)
13 * any later version.
14 *
15 * transcode is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with GNU Make; see the file COPYING. If not, write to
22 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 */
25
26 #define MOD_NAME "import_im.so"
27 #define MOD_VERSION "v0.1.3 (2008-10-07)"
28 #define MOD_CODEC "(video) RGB"
29
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 /* Note: because of ImageMagick bogosity, this must be included first, so
35 * we can undefine the PACKAGE_* symbols it splats into our namespace */
36 #ifdef HAVE_BROKEN_WAND
37 #include <wand/magick-wand.h>
38 #else /* we have a SANE wand header */
39 #include <wand/MagickWand.h>
40 #endif /* HAVE_BROKEN_WAND */
41
42 #undef PACKAGE_BUGREPORT
43 #undef PACKAGE_NAME
44 #undef PACKAGE_STRING
45 #undef PACKAGE_TARNAME
46 #undef PACKAGE_VERSION
47
48 #include "transcode.h"
49 #include "libtc/optstr.h"
50
51 #include <stdlib.h>
52 #include <stdio.h>
53
54 /*%*
55 *%* DESCRIPTION
56 *%* This module reads single images from disk using ImageMagick;
57 *%* a stream of correlated images can be automatically read if
58 *%* their filenames contains a common prefix and a serial number.
59 *%* All formats supported by ImageMagick are supported as well.
60 *%*
61 *%* BUILD-DEPENDS
62 *%* libMagick >= 6.2.4.0
63 *%*
64 *%* DEPENDS
65 *%* libMagick >= 6.2.4.0
66 *%*
67 *%* PROCESSING
68 *%* import/demuxer
69 *%*
70 *%* MEDIA
71 *%* video
72 *%*
73 *%* #INPUT
74 *%*
75 *%* OUTPUT
76 *%* RGB24
77 *%*
78 *%* OPTION
79 *%* noseq (flag)
80 *%* disable internal auto loading of images with similar names.
81 *%*/
82
83 static int verbose_flag = TC_QUIET;
84 static int capability_flag = TC_CAP_RGB|TC_CAP_VID;
85
86 #define MOD_PRE im
87 #include "import_def.h"
88
89 #include <time.h>
90 #include <sys/types.h>
91 #include <regex.h>
92
93
94 static char *head = NULL, *tail = NULL;
95 static int first_frame = 0, current_frame = 0, decoded_frame = 0, pad = 0;
96 static int total_frame = 0;
97 static int width = 0, height = 0;
98 static MagickWand *wand = NULL;
99 static int auto_seq_read = TC_TRUE;
100 /*
101 * automagically read further images with filename like the first one
102 * enabled by default for backward compatibility, but obsoleted
103 * by core option --multi_input
104 */
105
TCHandleMagickError(MagickWand * wand)106 static int TCHandleMagickError(MagickWand *wand)
107 {
108 ExceptionType severity;
109 const char *description = MagickGetException(wand, &severity);
110
111 tc_log_error(MOD_NAME, "%s", description);
112
113 MagickRelinquishMemory((void*)description);
114 return TC_IMPORT_ERROR;
115 }
116
117
118 /* ------------------------------------------------------------
119 *
120 * open stream
121 *
122 * ------------------------------------------------------------*/
123
124
125 /* I suspect we have a lot of potential memleaks in here -- FRomani */
126 MOD_open
127 {
128 int result, slen = 0;
129 char *regex = NULL, *frame = NULL;
130 regex_t preg;
131 regmatch_t pmatch[4];
132
133 if (param->flag == TC_AUDIO) {
134 return TC_IMPORT_OK;
135 }
136
137 if (param->flag == TC_VIDEO) {
138 param->fd = NULL;
139
140 // get the frame name and range
141 regex = "\\([^0-9]\\+[-._]\\?\\)\\?\\([0-9]\\+\\)\\([-._].\\+\\)\\?";
142 result = regcomp(&preg, regex, 0);
143 if (result) {
144 tc_log_perror(MOD_NAME, "ERROR: Regex compile failed.\n");
145 return TC_IMPORT_ERROR;
146 }
147
148 result = regexec(&preg, vob->video_in_file, 4, pmatch, 0);
149 if (result) {
150 tc_log_warn(MOD_NAME, "Regex match failed: no image sequence");
151 slen = strlen(vob->video_in_file) + 1;
152 head = tc_malloc(slen);
153 if (head == NULL) {
154 tc_log_perror(MOD_NAME, "filename head");
155 return TC_IMPORT_ERROR;
156 }
157 strlcpy(head, vob->video_in_file, slen);
158 tail = tc_malloc(1); /* URGH -- FRomani */
159 tail[0] = 0;
160 first_frame = -1;
161 } else {
162 // split the name into head, frame number, and tail
163 slen = pmatch[1].rm_eo - pmatch[1].rm_so + 1;
164 head = tc_malloc(slen);
165 if (head == NULL) {
166 tc_log_perror(MOD_NAME, "filename head");
167 return TC_IMPORT_ERROR;
168 }
169 strlcpy(head, vob->video_in_file, slen);
170
171 slen = pmatch[2].rm_eo - pmatch[2].rm_so + 1;
172 frame = tc_malloc(slen);
173 if (frame == NULL) {
174 tc_log_perror(MOD_NAME, "filename frame");
175 return TC_IMPORT_ERROR;
176 }
177 strlcpy(frame, vob->video_in_file + pmatch[2].rm_so, slen);
178
179 // If the frame number is padded with zeros, record how many digits
180 // are actually being used.
181 if (frame[0] == '0') {
182 pad = pmatch[2].rm_eo - pmatch[2].rm_so;
183 }
184 first_frame = atoi(frame);
185
186 slen = pmatch[3].rm_eo - pmatch[3].rm_so + 1;
187 tail = tc_malloc(slen);
188 if (tail == NULL) {
189 tc_log_perror(MOD_NAME, "filename tail");
190 return TC_IMPORT_ERROR;
191 }
192 strlcpy(tail, vob->video_in_file + pmatch[3].rm_so, slen);
193
194 tc_free(frame);
195 }
196
197 if (vob->im_v_string != NULL) {
198 if (optstr_lookup(vob->im_v_string, "noseq")) {
199 auto_seq_read = TC_FALSE;
200 if (verbose > TC_INFO) {
201 tc_log_info(MOD_NAME, "automagic image sequential read disabled");
202 }
203 }
204 }
205
206 current_frame = first_frame;
207 decoded_frame = 0;
208 width = vob->im_v_width;
209 height = vob->im_v_height;
210
211 if (total_frame == 0) {
212 /* only the very first time */
213 MagickWandGenesis();
214 }
215
216 wand = NewMagickWand();
217 if (wand == NULL) {
218 tc_log_error(MOD_NAME, "cannot create magick wand");
219 return TC_IMPORT_ERROR;
220 }
221
222 return TC_IMPORT_OK;
223 }
224
225 return TC_IMPORT_ERROR;
226 }
227
228
229 /* ------------------------------------------------------------
230 *
231 * decode stream
232 *
233 * ------------------------------------------------------------*/
234
235 MOD_decode
236 {
237 char *frame = NULL, *filename = NULL;
238 int slen;
239 MagickBooleanType status;
240
241 if (param->flag == TC_AUDIO) {
242 return TC_IMPORT_OK;
243 }
244
245 if (param->flag == TC_VIDEO) {
246 if (!auto_seq_read) {
247 if (decoded_frame > 0) {
248 return TC_IMPORT_ERROR;
249 }
250 filename = tc_strdup(vob->video_in_file);
251 } else {
252 // build the filename for the current frame
253 slen = strlen(head) + pad + strlen(tail) + 1;
254 filename = tc_malloc(slen);
255 if (pad) {
256 char framespec[10];
257 frame = tc_malloc(pad+1);
258 tc_snprintf(framespec, 10, "%%0%dd", pad);
259 tc_snprintf(frame, pad+1, framespec, current_frame);
260 frame[pad] = '\0';
261 } else if (first_frame >= 0) {
262 frame = tc_malloc(10);
263 tc_snprintf(frame, 10, "%d", current_frame);
264 }
265 strlcpy(filename, head, slen);
266 if (frame != NULL) {
267 strlcat(filename, frame, slen);
268 tc_free(frame);
269 frame = NULL;
270 }
271 strlcat(filename, tail, slen);
272 }
273
274 ClearMagickWand(wand);
275 /*
276 * This avoids IM to buffer all read images.
277 * I'm quite sure that this can be done in a smarter way,
278 * but I haven't yet figured out how. -- FRomani
279 */
280
281 status = MagickReadImage(wand, filename);
282 if (status == MagickFalse) {
283 if (auto_seq_read) {
284 /* let's assume that image sequence ends here */
285 return TC_IMPORT_ERROR;
286 }
287 return TCHandleMagickError(wand);
288 }
289
290 MagickSetLastIterator(wand);
291
292 status = MagickGetImagePixels(wand,
293 0, 0, width, height,
294 "RGB", CharPixel,
295 param->buffer);
296 /* param->size already set correctly by caller */
297 if (status == MagickFalse) {
298 return TCHandleMagickError(wand);
299 }
300
301 param->attributes |= TC_FRAME_IS_KEYFRAME;
302
303 total_frame++;
304 current_frame++;
305 decoded_frame++;
306
307 tc_free(filename);
308
309 return TC_IMPORT_OK;
310 }
311 return TC_IMPORT_ERROR;
312 }
313
314 /* ------------------------------------------------------------
315 *
316 * close stream
317 *
318 * ------------------------------------------------------------*/
319
320 MOD_close
321 {
322 if (param->flag == TC_AUDIO) {
323 return TC_IMPORT_OK;
324 }
325
326 if (param->flag == TC_VIDEO) {
327 vob_t *vob = tc_get_vob();
328
329 if (param->fd != NULL)
330 pclose(param->fd);
331 if (head != NULL)
332 tc_free(head);
333 if (tail != NULL)
334 tc_free(tail);
335
336 if (wand != NULL) {
337 DestroyMagickWand(wand);
338 wand = NULL;
339
340 if (!tc_has_more_video_in_file(vob)) {
341 /* ...can you hear that? is the sound of the ugliness... */
342 MagickWandTerminus();
343 }
344 }
345 return TC_IMPORT_OK;
346 }
347 return TC_IMPORT_ERROR;
348 }
349
350 /*************************************************************************/
351
352 /*
353 * Local variables:
354 * c-file-style: "stroustrup"
355 * c-file-offsets: ((case-label . *) (statement-case-intro . *))
356 * indent-tabs-mode: nil
357 * End:
358 *
359 * vim: expandtab shiftwidth=4:
360 */
361
362