Lines 1-32
Link Here
|
1 |
/* |
1 |
/* |
2 |
* import_im.c |
2 |
* import_im.c |
3 |
* |
3 |
* |
4 |
* Copyright (C) Thomas Östreich - June 2001 |
4 |
* Copyright (C) Thomas Oestreich - June 2001 |
|
|
5 |
* port to MagickWand API: |
6 |
* Copyright (C) Francesco Romani - July 2007 |
5 |
* |
7 |
* |
6 |
* This file is part of transcode, a video stream processing tool |
8 |
* This file is part of transcode, a video stream processing tool |
7 |
* |
9 |
* |
8 |
* transcode is free software; you can redistribute it and/or modify |
10 |
* transcode is free software; you can redistribute it and/or modify |
9 |
* it under the terms of the GNU General Public License as published by |
11 |
* it under the terms of the GNU General Public License as published by |
10 |
* the Free Software Foundation; either version 2, or (at your option) |
12 |
* the Free Software Foundation; either version 2, or (at your option) |
11 |
* any later version. |
13 |
* any later version. |
12 |
* |
14 |
* |
13 |
* transcode is distributed in the hope that it will be useful, |
15 |
* transcode is distributed in the hope that it will be useful, |
14 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 |
* GNU General Public License for more details. |
18 |
* GNU General Public License for more details. |
17 |
* |
19 |
* |
18 |
* You should have received a copy of the GNU General Public License |
20 |
* You should have received a copy of the GNU General Public License |
19 |
* along with GNU Make; see the file COPYING. If not, write to |
21 |
* along with GNU Make; see the file COPYING. If not, write to |
20 |
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. |
22 |
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. |
21 |
* |
23 |
* |
22 |
*/ |
24 |
*/ |
23 |
|
25 |
|
24 |
#define MOD_NAME "import_im.so" |
26 |
#define MOD_NAME "import_im.so" |
25 |
#define MOD_VERSION "v0.0.4 (2003-09-15)" |
27 |
#define MOD_VERSION "v0.1.0 (2007-07-17)" |
26 |
#define MOD_CODEC "(video) RGB" |
28 |
#define MOD_CODEC "(video) RGB" |
27 |
|
29 |
|
|
|
30 |
/* Note: because of ImageMagick bogosity, this must be included first, so |
31 |
* we can undefine the PACKAGE_* symbols it splats into our namespace */ |
32 |
#include <wand/MagickWand.h> |
33 |
#undef PACKAGE_BUGREPORT |
34 |
#undef PACKAGE_NAME |
35 |
#undef PACKAGE_STRING |
36 |
#undef PACKAGE_TARNAME |
37 |
#undef PACKAGE_VERSION |
38 |
|
28 |
#include "transcode.h" |
39 |
#include "transcode.h" |
29 |
|
40 |
|
|
|
41 |
#include <stdlib.h> |
42 |
#include <stdio.h> |
43 |
|
30 |
static int verbose_flag = TC_QUIET; |
44 |
static int verbose_flag = TC_QUIET; |
31 |
static int capability_flag = TC_CAP_RGB | TC_CAP_VID; |
45 |
static int capability_flag = TC_CAP_RGB | TC_CAP_VID; |
32 |
|
46 |
|
Lines 37-312
Link Here
|
37 |
#include <sys/types.h> |
51 |
#include <sys/types.h> |
38 |
#include <regex.h> |
52 |
#include <regex.h> |
39 |
|
53 |
|
40 |
/* transcode defines these as well as ImageMagick. */ |
|
|
41 |
#undef PACKAGE_NAME |
42 |
#undef PACKAGE_TARNAME |
43 |
#undef PACKAGE_VERSION |
44 |
#undef PACKAGE_STRING |
45 |
|
54 |
|
46 |
#include <magick/api.h> |
55 |
static char *head = NULL, *tail = NULL; |
|
|
56 |
static int first_frame = 0, last_frame = 0, current_frame = 0, pad = 0; |
57 |
static int width = 0, height = 0; |
58 |
static MagickWand *wand = NULL; |
47 |
|
59 |
|
48 |
|
60 |
|
49 |
extern int errno; |
61 |
static int TCHandleMagickError(MagickWand *wand) |
|
|
62 |
{ |
63 |
ExceptionType severity; |
64 |
const char *description = MagickGetException(wand, &severity); |
65 |
|
66 |
tc_log_error(MOD_NAME, "%s", description); |
50 |
|
67 |
|
51 |
char |
68 |
MagickRelinquishMemory((void*)description); |
52 |
*head = NULL, |
69 |
return TC_IMPORT_ERROR; |
53 |
*tail = NULL; |
70 |
} |
54 |
|
71 |
|
55 |
int |
|
|
56 |
first_frame = 0, |
57 |
last_frame = 0, |
58 |
current_frame = 0, |
59 |
pad = 0; |
60 |
|
72 |
|
61 |
|
73 |
/* ------------------------------------------------------------ |
62 |
/* ------------------------------------------------------------ |
|
|
63 |
* |
74 |
* |
64 |
* open stream |
75 |
* open stream |
65 |
* |
76 |
* |
66 |
* ------------------------------------------------------------*/ |
77 |
* ------------------------------------------------------------*/ |
67 |
|
78 |
|
|
|
79 |
|
80 |
/* I suspect we have a lot of potential memleaks in here -- FRomani */ |
68 |
MOD_open |
81 |
MOD_open |
69 |
{ |
82 |
{ |
70 |
int |
83 |
int result, string_length; |
71 |
result, |
84 |
long sret; |
72 |
string_length; |
85 |
char *regex, *frame, *filename; |
73 |
|
86 |
char printfspec[20]; |
74 |
long |
87 |
regex_t preg; |
75 |
sret; |
88 |
regmatch_t pmatch[4]; |
76 |
|
|
|
77 |
char |
78 |
*regex, |
79 |
*frame, |
80 |
*filename; |
81 |
|
82 |
char |
83 |
printfspec[20]; |
84 |
|
85 |
regex_t |
86 |
preg; |
87 |
|
88 |
regmatch_t |
89 |
pmatch[4]; |
90 |
|
91 |
if(param->flag == TC_AUDIO) { |
92 |
return(TC_IMPORT_OK); |
93 |
} |
94 |
|
95 |
if(param->flag == TC_VIDEO) { |
96 |
|
97 |
param->fd = NULL; |
98 |
|
89 |
|
99 |
// get the frame name and range |
90 |
if (param->flag == TC_AUDIO) { |
100 |
regex = "\\(.\\+[-._]\\)\\?\\([0-9]\\+\\)\\([-._].\\+\\)\\?"; |
91 |
return TC_IMPORT_OK; |
101 |
result = regcomp(&preg, regex, 0); |
|
|
102 |
if (result) { |
103 |
perror("ERROR: Regex compile failed.\n"); |
104 |
return(TC_IMPORT_ERROR); |
105 |
} |
92 |
} |
106 |
|
93 |
|
107 |
result = regexec(&preg, vob->video_in_file, 4, pmatch, 0); |
94 |
if (param->flag == TC_VIDEO) { |
108 |
if (result) { |
95 |
param->fd = NULL; |
109 |
fprintf(stderr, "Regex match failed: no image sequence\n"); |
|
|
110 |
string_length = strlen(vob->video_in_file) + 1; |
111 |
if ((head = malloc(string_length)) == NULL) { |
112 |
perror("filename head"); |
113 |
return(TC_IMPORT_ERROR); |
114 |
} |
115 |
strlcpy(head, vob->video_in_file, string_length); |
116 |
tail = malloc(1); |
117 |
tail[0] = 0; |
118 |
first_frame = -1; |
119 |
last_frame = 0x7fffffff; |
120 |
} |
121 |
else { |
122 |
// split the name into head, frame number, and tail |
123 |
string_length = pmatch[1].rm_eo - pmatch[1].rm_so + 1; |
124 |
if ((head = malloc(string_length)) == NULL) { |
125 |
perror("filename head"); |
126 |
return(TC_IMPORT_ERROR); |
127 |
} |
128 |
strlcpy(head, vob->video_in_file, string_length); |
129 |
|
96 |
|
130 |
string_length = pmatch[2].rm_eo - pmatch[2].rm_so + 1; |
97 |
// get the frame name and range |
131 |
if ((frame = malloc(string_length)) == NULL) { |
98 |
regex = "\\(.\\+[-._]\\)\\?\\([0-9]\\+\\)\\([-._].\\+\\)\\?"; |
132 |
perror("filename frame"); |
99 |
result = regcomp(&preg, regex, 0); |
133 |
return(TC_IMPORT_ERROR); |
100 |
if (result) { |
|
|
101 |
tc_log_perror(MOD_NAME, "ERROR: Regex compile failed.\n"); |
102 |
return TC_IMPORT_ERROR; |
134 |
} |
103 |
} |
135 |
strlcpy(frame, vob->video_in_file + pmatch[2].rm_so, string_length); |
|
|
136 |
|
104 |
|
137 |
// If the frame number is padded with zeros, record how many digits |
105 |
result = regexec(&preg, vob->video_in_file, 4, pmatch, 0); |
138 |
// are actually being used. |
106 |
if (result) { |
139 |
if (frame[0] == '0') { |
107 |
tc_log_warn(MOD_NAME, "Regex match failed: no image sequence"); |
140 |
pad = pmatch[2].rm_eo - pmatch[2].rm_so; |
108 |
string_length = strlen(vob->video_in_file) + 1; |
|
|
109 |
head = tc_malloc(string_length); |
110 |
if (head == NULL) { |
111 |
tc_log_perror(MOD_NAME, "filename head"); |
112 |
return TC_IMPORT_ERROR; |
113 |
} |
114 |
strlcpy(head, vob->video_in_file, string_length); |
115 |
tail = tc_malloc(1); /* URGH -- FRomani */ |
116 |
tail[0] = 0; |
117 |
first_frame = -1; |
118 |
last_frame = 0x7fffffff; |
119 |
} else { |
120 |
// split the name into head, frame number, and tail |
121 |
string_length = pmatch[1].rm_eo - pmatch[1].rm_so + 1; |
122 |
head = tc_malloc(string_length); |
123 |
if (head == NULL) { |
124 |
tc_log_perror(MOD_NAME, "filename head"); |
125 |
return TC_IMPORT_ERROR; |
126 |
} |
127 |
strlcpy(head, vob->video_in_file, string_length); |
128 |
|
129 |
string_length = pmatch[2].rm_eo - pmatch[2].rm_so + 1; |
130 |
frame = tc_malloc(string_length); |
131 |
if (frame == NULL) { |
132 |
tc_log_perror(MOD_NAME, "filename frame"); |
133 |
return TC_IMPORT_ERROR; |
134 |
} |
135 |
strlcpy(frame, vob->video_in_file + pmatch[2].rm_so, string_length); |
136 |
|
137 |
// If the frame number is padded with zeros, record how many digits |
138 |
// are actually being used. |
139 |
if (frame[0] == '0') { |
140 |
pad = pmatch[2].rm_eo - pmatch[2].rm_so; |
141 |
} |
142 |
first_frame = atoi(frame); |
143 |
|
144 |
string_length = pmatch[3].rm_eo - pmatch[3].rm_so + 1; |
145 |
tail = tc_malloc(string_length); |
146 |
if (tail == NULL) { |
147 |
tc_log_perror(MOD_NAME, "filename tail"); |
148 |
return TC_IMPORT_ERROR; |
149 |
} |
150 |
strlcpy(tail, vob->video_in_file + pmatch[3].rm_so, string_length); |
151 |
|
152 |
// find the last frame by trying to open files |
153 |
last_frame = first_frame; |
154 |
filename = tc_malloc(strlen(head) + pad + strlen(tail) + 1); |
155 |
/* why remalloc frame? */ |
156 |
/* frame = malloc(pad + 1); */ |
157 |
do { |
158 |
last_frame++; |
159 |
tc_snprintf(printfspec, sizeof(printfspec), "%%s%%0%dd%%s", pad); |
160 |
string_length = strlen(head) + pad + strlen(tail) + 1; |
161 |
sret = tc_snprintf(filename, string_length, printfspec, head, |
162 |
last_frame, tail); |
163 |
if (sret < 0) |
164 |
return TC_IMPORT_ERROR; |
165 |
} while (close(open(filename, O_RDONLY)) != -1); /* URGH -- Fromani */ |
166 |
last_frame--; |
167 |
tc_free(filename); |
168 |
tc_free(frame); |
141 |
} |
169 |
} |
142 |
first_frame = atoi(frame); |
|
|
143 |
|
170 |
|
144 |
string_length = pmatch[3].rm_eo - pmatch[3].rm_so + 1; |
171 |
current_frame = first_frame; |
145 |
if ((tail = malloc(string_length)) == NULL) { |
|
|
146 |
perror("filename tail"); |
147 |
return(TC_IMPORT_ERROR); |
148 |
} |
149 |
strlcpy(tail, vob->video_in_file + pmatch[3].rm_so, string_length); |
150 |
|
172 |
|
151 |
// find the last frame by trying to open files |
173 |
width = vob->im_v_width; |
152 |
last_frame = first_frame; |
174 |
height = vob->im_v_height; |
153 |
filename = malloc(strlen(head) + pad + strlen(tail) + 1); |
|
|
154 |
/* why remalloc frame? */ |
155 |
/* frame = malloc(pad + 1); */ |
156 |
do { |
157 |
last_frame++; |
158 |
snprintf(printfspec, sizeof(printfspec), "%%s%%0%dd%%s", pad); |
159 |
string_length = strlen(head) + pad + strlen(tail) + 1; |
160 |
sret = snprintf(filename, string_length, printfspec, head, |
161 |
last_frame, tail); |
162 |
if (tc_test_string(__FILE__, __LINE__, string_length, sret, errno)) |
163 |
return(TC_IMPORT_ERROR); |
164 |
} while (close(open(filename, O_RDONLY)) != -1); |
165 |
last_frame--; |
166 |
free(filename); |
167 |
free(frame); |
168 |
} |
169 |
|
175 |
|
170 |
current_frame = first_frame; |
176 |
MagickWandGenesis(); |
|
|
177 |
wand = NewMagickWand(); |
171 |
|
178 |
|
172 |
// initialize ImageMagick |
179 |
if (wand == NULL) { |
173 |
InitializeMagick(""); |
180 |
tc_log_error(MOD_NAME, "cannot create magick wand"); |
|
|
181 |
return TC_IMPORT_ERROR; |
182 |
} |
183 |
MagickResetIterator(wand); /* this needs to be done once */ |
174 |
|
184 |
|
175 |
return(TC_IMPORT_OK); |
185 |
return TC_IMPORT_OK; |
176 |
} |
186 |
} |
177 |
|
187 |
|
178 |
return(TC_IMPORT_ERROR); |
188 |
return TC_IMPORT_ERROR; |
179 |
} |
189 |
} |
180 |
|
190 |
|
181 |
|
191 |
|
182 |
/* ------------------------------------------------------------ |
192 |
/* ------------------------------------------------------------ |
183 |
* |
193 |
* |
184 |
* decode stream |
194 |
* decode stream |
185 |
* |
195 |
* |
186 |
* ------------------------------------------------------------*/ |
196 |
* ------------------------------------------------------------*/ |
187 |
|
197 |
|
188 |
MOD_decode { |
198 |
MOD_decode |
189 |
|
199 |
{ |
190 |
ExceptionInfo |
200 |
char *filename = NULL, *frame = NULL; |
191 |
exception_info; |
201 |
int string_length; |
192 |
|
202 |
MagickBooleanType status; |
193 |
ImageInfo |
|
|
194 |
*image_info; |
195 |
|
203 |
|
196 |
Image |
204 |
if (param->flag == TC_AUDIO) { |
197 |
*image; |
205 |
return TC_IMPORT_OK; |
198 |
|
|
|
199 |
PixelPacket |
200 |
*pixel_packet; |
201 |
|
202 |
char |
203 |
*filename = NULL, |
204 |
*frame = NULL, |
205 |
*framespec = NULL; |
206 |
|
207 |
int |
208 |
column, |
209 |
row, |
210 |
string_length; |
211 |
|
212 |
|
213 |
if (current_frame > last_frame) |
214 |
return(TC_IMPORT_ERROR); |
215 |
|
216 |
// build the filename for the current frame |
217 |
string_length = strlen(head) + pad + strlen(tail) + 1; |
218 |
filename = malloc(string_length); |
219 |
if (pad) { |
220 |
frame = malloc(pad+1); |
221 |
framespec = malloc(10); |
222 |
snprintf(framespec, 10, "%%0%dd", pad); |
223 |
snprintf(frame, pad+1, framespec, current_frame); |
224 |
free(framespec); |
225 |
frame[pad] = '\0'; |
226 |
} |
227 |
else if (first_frame >= 0) { |
228 |
frame = malloc(10); |
229 |
snprintf(frame, 10, "%d", current_frame); |
230 |
} |
206 |
} |
231 |
strlcpy(filename, head, string_length); |
|
|
232 |
if (frame != NULL) { |
233 |
strlcat(filename, frame, string_length); |
234 |
free(frame); |
235 |
frame = NULL; |
236 |
} |
237 |
strlcat(filename, tail, string_length); |
238 |
|
207 |
|
239 |
// Have ImageMagick open the file and read in the image data. |
208 |
if (param->flag == TC_VIDEO) { |
240 |
GetExceptionInfo(&exception_info); |
209 |
if (current_frame > last_frame) |
241 |
image_info=CloneImageInfo((ImageInfo *) NULL); |
210 |
return TC_IMPORT_ERROR; |
242 |
(void) strlcpy(image_info->filename, filename, MaxTextExtent); |
211 |
|
243 |
image=ReadImage(image_info,&exception_info); |
212 |
// build the filename for the current frame |
244 |
if (image == (Image *) NULL) { |
213 |
string_length = strlen(head) + pad + strlen(tail) + 1; |
245 |
MagickError(exception_info.severity, |
214 |
filename = tc_malloc(string_length); |
246 |
exception_info.reason, |
215 |
if (pad) { |
247 |
exception_info.description); |
216 |
char framespec[10]; |
248 |
// skip |
217 |
frame = tc_malloc(pad+1); |
249 |
return (TC_IMPORT_ERROR); |
218 |
tc_snprintf(framespec, 10, "%%0%dd", pad); |
250 |
} |
219 |
tc_snprintf(frame, pad+1, framespec, current_frame); |
|
|
220 |
frame[pad] = '\0'; |
221 |
} else if (first_frame >= 0) { |
222 |
frame = tc_malloc(10); |
223 |
tc_snprintf(frame, 10, "%d", current_frame); |
224 |
} |
225 |
strlcpy(filename, head, string_length); |
226 |
if (frame != NULL) { |
227 |
strlcat(filename, frame, string_length); |
228 |
tc_free(frame); |
229 |
frame = NULL; |
230 |
} |
231 |
strlcat(filename, tail, string_length); |
251 |
|
232 |
|
252 |
/* |
233 |
ClearMagickWand(wand); |
253 |
* Copy the pixels into a buffer in RGB order |
234 |
/* |
254 |
*/ |
235 |
* This avoids IM to buffer all read images. |
255 |
pixel_packet = GetImagePixels(image, 0, 0, image->columns, image->rows); |
236 |
* I'm quite sure that this can be done in a smarter way, |
256 |
for (row = 0; row < image->rows; row++) { |
237 |
* but I haven't yet figured out how. -- FRomani |
257 |
for (column = 0; column < image->columns; column++) { |
238 |
*/ |
258 |
/* |
239 |
|
259 |
* The bit-shift 8 in the following lines is to convert |
240 |
status = MagickReadImage(wand, filename); |
260 |
* 16-bit-per-channel images that may be read by ImageMagick |
241 |
if (status == MagickFalse) { |
261 |
* into the 8-bit-per-channel images that transcode uses. |
242 |
return TCHandleMagickError(wand); |
262 |
* The bit-shift is still valid for 8-bit-per-channel images |
|
|
263 |
* because when ImageMagick handles 8-bit images it still uses |
264 |
* unsigned shorts, but stores the same 8-bit value in both |
265 |
* the low and high byte. |
266 |
*/ |
267 |
param->buffer[(row * image->columns + column) * 3 + 0] = |
268 |
(char) (pixel_packet[(image->rows - row - 1) * image->columns + |
269 |
column].blue >> 8); |
270 |
param->buffer[(row * image->columns + column) * 3 + 1] = |
271 |
(char) (pixel_packet[(image->rows - row - 1) * image->columns + |
272 |
column].green >> 8); |
273 |
param->buffer[(row * image->columns + column) * 3 + 2] = |
274 |
(char) (pixel_packet[(image->rows - row - 1) * image->columns + |
275 |
column].red >> 8); |
276 |
} |
243 |
} |
277 |
} |
|
|
278 |
|
244 |
|
279 |
if (current_frame == first_frame) |
245 |
MagickSetLastIterator(wand); |
280 |
param->attributes |= TC_FRAME_IS_KEYFRAME; |
246 |
|
|
|
247 |
status = MagickGetImagePixels(wand, |
248 |
0, 0, width, height, |
249 |
"RGB", CharPixel, |
250 |
param->buffer); |
251 |
/* param->size already set correctly by caller */ |
252 |
if (status == MagickFalse) { |
253 |
return TCHandleMagickError(wand); |
254 |
} |
281 |
|
255 |
|
282 |
current_frame++; |
256 |
if (current_frame == first_frame) |
|
|
257 |
param->attributes |= TC_FRAME_IS_KEYFRAME; |
283 |
|
258 |
|
284 |
// How do we do this? The next line is not right (segfaults) |
259 |
current_frame++; |
285 |
// I can't find a DestroyPixelPacket() method. |
260 |
|
286 |
//free(pixel_packet); |
261 |
tc_free(filename); |
287 |
DestroyImage(image); |
|
|
288 |
DestroyImageInfo(image_info); |
289 |
DestroyExceptionInfo(&exception_info); |
290 |
free(filename); |
291 |
|
262 |
|
292 |
return(TC_IMPORT_OK); |
263 |
return TC_IMPORT_OK; |
|
|
264 |
} |
265 |
return TC_IMPORT_ERROR; |
293 |
} |
266 |
} |
294 |
|
267 |
|
295 |
/* ------------------------------------------------------------ |
268 |
/* ------------------------------------------------------------ |
296 |
* |
269 |
* |
297 |
* close stream |
270 |
* close stream |
298 |
* |
271 |
* |
299 |
* ------------------------------------------------------------*/ |
272 |
* ------------------------------------------------------------*/ |
300 |
|
273 |
|
301 |
MOD_close |
274 |
MOD_close |
302 |
{ |
275 |
{ |
303 |
if (param->fd != NULL) pclose(param->fd); |
276 |
if (param->flag == TC_AUDIO) { |
304 |
if (head != NULL) free(head); |
277 |
return TC_IMPORT_OK; |
305 |
if (tail != NULL) free(tail); |
278 |
} |
306 |
|
|
|
307 |
DestroyMagick(); |
308 |
|
279 |
|
309 |
return(TC_IMPORT_OK); |
280 |
if (param->flag == TC_VIDEO) { |
|
|
281 |
if (param->fd != NULL) |
282 |
pclose(param->fd); |
283 |
if (head != NULL) |
284 |
tc_free(head); |
285 |
if (tail != NULL) |
286 |
tc_free(tail); |
287 |
|
288 |
if (wand != NULL) { |
289 |
DestroyMagickWand(wand); |
290 |
MagickWandTerminus(); |
291 |
wand = NULL; |
292 |
} |
293 |
return TC_IMPORT_OK; |
294 |
} |
295 |
return TC_IMPORT_ERROR; |
310 |
} |
296 |
} |
311 |
|
297 |
|
|
|
298 |
/*************************************************************************/ |
312 |
|
299 |
|
|
|
300 |
/* |
301 |
* Local variables: |
302 |
* c-file-style: "stroustrup" |
303 |
* c-file-offsets: ((case-label . *) (statement-case-intro . *)) |
304 |
* indent-tabs-mode: nil |
305 |
* End: |
306 |
* |
307 |
* vim: expandtab shiftwidth=4: |
308 |
*/ |