Commit | Line | Data |
---|---|---|
80f575fc DM |
1 | /* |
2 | * filter_transform.c | |
3 | * | |
4 | * Copyright (C) Georg Martius - June 2007 | |
5 | * georg dot martius at web dot de | |
6 | * | |
7 | * This file is part of transcode, a video stream processing tool | |
8 | * | |
9 | * transcode is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License as published by | |
11 | * the Free Software Foundation; either version 2, or (at your option) | |
12 | * any later version. | |
13 | * | |
14 | * transcode is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | * GNU General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU General Public License | |
20 | * along with GNU Make; see the file COPYING. If not, write to | |
21 | * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | |
22 | * | |
23 | * Typical call: | |
24 | * transcode -J transform -i inp.mpeg -y xdiv,tcaud inp_stab.avi | |
25 | */ | |
26 | ||
27 | #include "libvidstab.h" | |
28 | ||
29 | #define MOD_NAME "filter_transform.so" | |
30 | #define MOD_VERSION LIBVIDSTAB_VERSION | |
31 | #define MOD_CAP "transforms each frame according to transformations\n\ | |
32 | given in an input file (e.g. translation, rotate) see also filter stabilize" | |
33 | #define MOD_AUTHOR "Georg Martius" | |
34 | #define MOD_FEATURES \ | |
35 | TC_MODULE_FEATURE_FILTER|TC_MODULE_FEATURE_VIDEO | |
36 | #define MOD_FLAGS \ | |
37 | TC_MODULE_FLAG_RECONFIGURABLE | |
38 | ||
39 | #include "transcode.h" | |
40 | #include "filter.h" | |
41 | ||
42 | #include "libtc/libtc.h" | |
43 | #include "libtc/optstr.h" | |
44 | #include "libtc/tccodecs.h" | |
45 | #include "libtc/tcmodule-plugin.h" | |
46 | ||
47 | #include "transcode_specifics.h" | |
48 | ||
49 | #define DEFAULT_TRANS_FILE_NAME "transforms.dat" | |
50 | ||
51 | typedef struct { | |
52 | VSTransformData td; | |
53 | vob_t* vob; // pointer to information structure | |
54 | ||
55 | VSTransformations trans; // transformations | |
56 | ||
57 | ||
58 | double sharpen; // amount of sharpening | |
59 | char input[TC_BUF_LINE]; | |
60 | char conf_str[TC_BUF_MIN]; | |
61 | } FilterData; | |
62 | ||
63 | /** | |
64 | * transform_init: Initialize this instance of the module. See | |
65 | * tcmodule-data.h for function details. | |
66 | */ | |
67 | static int transform_init(TCModuleInstance *self, uint32_t features) | |
68 | { | |
69 | FilterData* fd = NULL; | |
70 | TC_MODULE_SELF_CHECK(self, "init"); | |
71 | TC_MODULE_INIT_CHECK(self, MOD_FEATURES, features); | |
72 | ||
73 | setLogFunctions(); | |
74 | ||
75 | fd = tc_zalloc(sizeof(FilterData)); | |
76 | if (fd == NULL) { | |
77 | tc_log_error(MOD_NAME, "init: out of memory!"); | |
78 | return TC_ERROR; | |
79 | } | |
80 | self->userdata = fd; | |
81 | if (verbose) { | |
82 | tc_log_info(MOD_NAME, "%s %s", MOD_VERSION, MOD_CAP); | |
83 | } | |
84 | ||
85 | return TC_OK; | |
86 | } | |
87 | ||
88 | ||
89 | /** | |
90 | * transform_configure: Configure this instance of the module. See | |
91 | * tcmodule-data.h for function details. | |
92 | */ | |
93 | static int transform_configure(TCModuleInstance *self, | |
94 | const char *options, vob_t *vob) | |
95 | { | |
96 | FilterData *fd = NULL; | |
97 | char* filenamecopy, *filebasename; | |
98 | FILE* f; | |
99 | TC_MODULE_SELF_CHECK(self, "configure"); | |
100 | ||
101 | fd = self->userdata; | |
102 | VSTransformData* td = &(fd->td); | |
103 | ||
104 | fd->vob = vob; | |
105 | if (!fd->vob) | |
106 | return TC_ERROR; /* cannot happen */ | |
107 | ||
108 | /**** Initialise private data structure */ | |
109 | ||
110 | VSFrameInfo fi_src; | |
111 | VSFrameInfo fi_dest; | |
112 | vsFrameInfoInit(&fi_src, fd->vob->ex_v_width, fd->vob->ex_v_height, | |
113 | transcode2ourPF(fd->vob->im_v_codec)); | |
114 | vsFrameInfoInit(&fi_dest, fd->vob->ex_v_width, fd->vob->ex_v_height, | |
115 | transcode2ourPF(fd->vob->im_v_codec)); | |
116 | ||
117 | VSTransformConfig conf = vsTransformGetDefaultConfig(MOD_NAME); | |
118 | conf.verbose = verbose; | |
119 | fd->sharpen = 0.8; | |
120 | ||
121 | ||
122 | vsTransformationsInit(&fd->trans); | |
123 | ||
124 | filenamecopy = tc_strdup(fd->vob->video_in_file); | |
125 | filebasename = basename(filenamecopy); | |
126 | if (strlen(filebasename) < TC_BUF_LINE - 4) { | |
127 | tc_snprintf(fd->input, TC_BUF_LINE, "%s.trf", filebasename); | |
128 | } else { | |
129 | tc_log_warn(MOD_NAME, "input name too long, using default `%s'", | |
130 | DEFAULT_TRANS_FILE_NAME); | |
131 | tc_snprintf(fd->input, TC_BUF_LINE, DEFAULT_TRANS_FILE_NAME); | |
132 | } | |
133 | ||
134 | ||
135 | ||
136 | /* process remaining options */ | |
137 | if (options != NULL) { | |
138 | // We support also the help option. | |
139 | if(optstr_lookup(options, "help")) { | |
140 | tc_log_info(MOD_NAME,vs_transform_help); | |
141 | return(TC_IMPORT_ERROR); | |
142 | } | |
143 | optstr_get(options, "input", "%[^:]", (char*)&fd->input); | |
144 | optstr_get(options, "maxshift", "%d", &conf.maxShift); | |
145 | optstr_get(options, "maxangle", "%lf", &conf.maxAngle); | |
146 | optstr_get(options, "smoothing", "%d", &conf.smoothing); | |
147 | optstr_get(options, "invert" , "%d", &conf.invert); | |
148 | optstr_get(options, "relative" , "%d", &conf.relative); | |
149 | optstr_get(options, "zoom" ,"%lf", &conf.zoom); | |
150 | optstr_get(options, "optzoom" , "%d", &conf.optZoom); | |
151 | optstr_get(options, "zoomspeed", "%lf",&conf.zoomSpeed); | |
152 | optstr_get(options, "interpol" , "%d", (int*)(&conf.interpolType)); | |
153 | optstr_get(options, "sharpen" ,"%lf", &fd->sharpen); | |
154 | if(optstr_lookup(options, "tripod")){ | |
155 | tc_log_info(MOD_NAME,"Virtual tripod mode: relative=False, smoothing=0"); | |
156 | conf.relative=0; | |
157 | conf.smoothing=0; | |
158 | } | |
159 | } | |
160 | ||
161 | if(vsTransformDataInit(td, &conf, &fi_src, &fi_dest) != VS_OK){ | |
162 | tc_log_error(MOD_NAME, "initialization of VSTransformData failed"); | |
163 | return TC_ERROR; | |
164 | } | |
165 | vsTransformGetConfig(&conf,td); | |
166 | ||
167 | if (verbose) { | |
168 | tc_log_info(MOD_NAME, "Image Transformation/Stabilization Settings:"); | |
169 | tc_log_info(MOD_NAME, " input = %s", fd->input); | |
170 | tc_log_info(MOD_NAME, " smoothing = %d", conf.smoothing); | |
171 | tc_log_info(MOD_NAME, " maxshift = %d", conf.maxShift); | |
172 | tc_log_info(MOD_NAME, " maxangle = %f", conf.maxAngle); | |
173 | tc_log_info(MOD_NAME, " crop = %s", | |
174 | conf.crop ? "Black" : "Keep"); | |
175 | tc_log_info(MOD_NAME, " relative = %s", | |
176 | conf.relative ? "True": "False"); | |
177 | tc_log_info(MOD_NAME, " invert = %s", | |
178 | conf.invert ? "True" : "False"); | |
179 | tc_log_info(MOD_NAME, " zoom = %f", conf.zoom); | |
180 | tc_log_info(MOD_NAME, " optzoom = %d", conf.optZoom); | |
181 | if(conf.optZoom==2){ | |
182 | tc_log_info(MOD_NAME, " zoomspeed = %f", conf.zoomSpeed); | |
183 | } | |
184 | tc_log_info(MOD_NAME, " interpol = %s", | |
185 | getInterpolationTypeName(conf.interpolType)); | |
186 | tc_log_info(MOD_NAME, " sharpen = %f", fd->sharpen); | |
187 | } | |
188 | ||
189 | f = fopen(fd->input, "r"); | |
190 | if (f == NULL) { | |
191 | tc_log_error(MOD_NAME, "cannot open input file %s!\n", fd->input); | |
192 | /* return (-1); when called using tcmodinfo this will fail */ | |
193 | } else { | |
194 | VSManyLocalMotions mlms; | |
195 | if(vsReadLocalMotionsFile(f,&mlms)==VS_OK){ | |
196 | // calculate the actual transforms from the localmotions | |
197 | if(vsLocalmotions2Transforms(td, &mlms,&fd->trans)!=VS_OK) | |
198 | tc_log_error(MOD_NAME, "calculating transformations failed!\n"); | |
199 | }else{ // try to read old format | |
200 | if (!vsReadOldTransforms(td, f, &fd->trans)) { /* read input file */ | |
201 | tc_log_error(MOD_NAME, "error parsing input file %s!\n", fd->input); | |
202 | } | |
203 | } | |
204 | } | |
205 | fclose(f); | |
206 | ||
207 | if (vsPreprocessTransforms(td, &fd->trans)!= VS_OK ) { | |
208 | tc_log_error(MOD_NAME, "error while preprocessing transforms!"); | |
209 | return TC_ERROR; | |
210 | } | |
211 | ||
212 | // sharpen is still in transcode... | |
213 | /* Is this the right point to add the filter? Seems to be the case.*/ | |
214 | if(fd->sharpen>0){ | |
215 | /* load unsharp filter */ | |
216 | char unsharp_param[256]; | |
217 | sprintf(unsharp_param,"luma=%f:%s:chroma=%f:%s", | |
218 | fd->sharpen, "luma_matrix=5x5", | |
219 | fd->sharpen/2, "chroma_matrix=5x5"); | |
220 | if (!tc_filter_add("unsharp", unsharp_param)) { | |
221 | tc_log_warn(MOD_NAME, "cannot load unsharp filter!"); | |
222 | } | |
223 | } | |
224 | ||
225 | return TC_OK; | |
226 | } | |
227 | ||
228 | ||
229 | /** | |
230 | * transform_filter_video: performs the transformation of frames | |
231 | * See tcmodule-data.h for function details. | |
232 | */ | |
233 | static int transform_filter_video(TCModuleInstance *self, | |
234 | vframe_list_t *frame) | |
235 | { | |
236 | FilterData *fd = NULL; | |
237 | ||
238 | TC_MODULE_SELF_CHECK(self, "filter_video"); | |
239 | TC_MODULE_SELF_CHECK(frame, "filter_video"); | |
240 | ||
241 | fd = self->userdata; | |
242 | VSFrame vsFrame; | |
243 | vsFrameFillFromBuffer(&vsFrame,frame->video_buf, vsTransformGetSrcFrameInfo(&fd->td)); | |
244 | ||
245 | vsTransformPrepare(&fd->td, &vsFrame, &vsFrame); | |
246 | ||
247 | VSTransform t = vsGetNextTransform(&fd->td, &fd->trans); | |
248 | ||
249 | vsDoTransform(&fd->td, t); | |
250 | ||
251 | vsTransformFinish(&fd->td); | |
252 | return TC_OK; | |
253 | } | |
254 | ||
255 | ||
256 | /** | |
257 | * transform_fini: Clean up after this instance of the module. See | |
258 | * tcmodule-data.h for function details. | |
259 | */ | |
260 | static int transform_fini(TCModuleInstance *self) | |
261 | { | |
262 | FilterData *fd = NULL; | |
263 | TC_MODULE_SELF_CHECK(self, "fini"); | |
264 | fd = self->userdata; | |
265 | tc_free(fd); | |
266 | self->userdata = NULL; | |
267 | return TC_OK; | |
268 | } | |
269 | ||
270 | ||
271 | /** | |
272 | * transform_stop: Reset this instance of the module. See tcmodule-data.h | |
273 | * for function details. | |
274 | */ | |
275 | static int transform_stop(TCModuleInstance *self) | |
276 | { | |
277 | FilterData *fd = NULL; | |
278 | TC_MODULE_SELF_CHECK(self, "stop"); | |
279 | fd = self->userdata; | |
280 | vsTransformDataCleanup(&fd->td); | |
281 | ||
282 | vsTransformationsCleanup(&fd->trans); | |
283 | return TC_OK; | |
284 | } | |
285 | ||
286 | /* checks for parameter in function _inspect */ | |
287 | #define CHECKPARAM(paramname, formatstring, variable) \ | |
288 | if (optstr_lookup(param, paramname)) { \ | |
289 | tc_snprintf(fd->conf_str, sizeof(fd->conf_str), \ | |
290 | formatstring, variable); \ | |
291 | *value = fd->conf_str; \ | |
292 | } | |
293 | ||
294 | /** | |
295 | * stabilize_inspect: Return the value of an option in this instance of | |
296 | * the module. See tcmodule-data.h for function details. | |
297 | */ | |
298 | static int transform_inspect(TCModuleInstance *self, | |
299 | const char *param, const char **value) | |
300 | { | |
301 | FilterData *fd = NULL; | |
302 | TC_MODULE_SELF_CHECK(self, "inspect"); | |
303 | TC_MODULE_SELF_CHECK(param, "inspect"); | |
304 | TC_MODULE_SELF_CHECK(value, "inspect"); | |
305 | ||
306 | fd = self->userdata; | |
307 | ||
308 | if (optstr_lookup(param, "help")) { | |
309 | *value = vs_transform_help; | |
310 | } | |
311 | VSTransformConfig conf; | |
312 | vsTransformGetConfig(&conf,&fd->td); | |
313 | CHECKPARAM("maxshift", "maxshift=%d", conf.maxShift); | |
314 | CHECKPARAM("maxangle", "maxangle=%f", conf.maxAngle); | |
315 | CHECKPARAM("smoothing","smoothing=%d", conf.smoothing); | |
316 | CHECKPARAM("crop", "crop=%d", conf.crop); | |
317 | CHECKPARAM("relative", "relative=%d", conf.relative); | |
318 | CHECKPARAM("invert", "invert=%i", conf.invert); | |
319 | CHECKPARAM("input", "input=%s", fd->input); | |
320 | CHECKPARAM("optzoom", "optzoom=%i", conf.optZoom); | |
321 | CHECKPARAM("zoom", "zoom=%f", conf.zoom); | |
322 | CHECKPARAM("sharpen", "sharpen=%f", fd->sharpen); | |
323 | ||
324 | return TC_OK; | |
325 | }; | |
326 | ||
327 | ||
328 | static const TCCodecID transform_codecs_in[] = { | |
329 | TC_CODEC_YUV420P, TC_CODEC_YUV422P, TC_CODEC_RGB, TC_CODEC_ERROR | |
330 | }; | |
331 | static const TCCodecID transform_codecs_out[] = { | |
332 | TC_CODEC_YUV420P, TC_CODEC_YUV422P, TC_CODEC_RGB, TC_CODEC_ERROR | |
333 | }; | |
334 | TC_MODULE_FILTER_FORMATS(transform); | |
335 | ||
336 | TC_MODULE_INFO(transform); | |
337 | ||
338 | static const TCModuleClass transform_class = { | |
339 | TC_MODULE_CLASS_HEAD(transform), | |
340 | ||
341 | .init = transform_init, | |
342 | .fini = transform_fini, | |
343 | .configure = transform_configure, | |
344 | .stop = transform_stop, | |
345 | .inspect = transform_inspect, | |
346 | ||
347 | .filter_video = transform_filter_video, | |
348 | }; | |
349 | ||
350 | TC_MODULE_ENTRY_POINT(transform) | |
351 | ||
352 | /*************************************************************************/ | |
353 | ||
354 | static int transform_get_config(TCModuleInstance *self, char *options) | |
355 | { | |
356 | TC_MODULE_SELF_CHECK(self, "get_config"); | |
357 | ||
358 | optstr_filter_desc(options, MOD_NAME, MOD_CAP, MOD_VERSION, | |
359 | MOD_AUTHOR, "VRY4", "1"); | |
360 | ||
361 | return TC_OK; | |
362 | } | |
363 | ||
364 | static int transform_process(TCModuleInstance *self, frame_list_t *frame) | |
365 | { | |
366 | TC_MODULE_SELF_CHECK(self, "process"); | |
367 | ||
368 | if (frame->tag & TC_PRE_S_PROCESS && frame->tag & TC_VIDEO) { | |
369 | return transform_filter_video(self, (vframe_list_t *)frame); | |
370 | } | |
371 | return TC_OK; | |
372 | } | |
373 | ||
374 | /*************************************************************************/ | |
375 | ||
376 | TC_FILTER_OLDINTERFACE(transform) | |
377 | ||
378 | /*************************************************************************/ | |
379 | ||
380 | /* | |
381 | * Local variables: | |
382 | * c-file-style: "stroustrup" | |
383 | * c-file-offsets: ((case-label . *) (statement-case-intro . *)) | |
384 | * indent-tabs-mode: nil | |
385 | * c-basic-offset: 4 t | |
386 | * End: | |
387 | * | |
388 | * vim: expandtab shiftwidth=4: | |
389 | */ |