1 /*
2 * frei0r_helper.c -- frei0r helper
3 * Copyright (c) 2008 Marco Gittler <g.marco@freenet.de>
4 * Copyright (C) 2009-2020 Meltytech, LLC
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20 #include "frei0r_helper.h"
21 #include <frei0r.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #ifdef _WIN32
25 #include <windows.h>
26 #endif
27
28 const char *CAIROBLEND_MODE_PROPERTY = "frei0r.cairoblend.mode";
29
rgba_bgra(uint8_t * src,uint8_t * dst,int width,int height)30 static void rgba_bgra( uint8_t *src, uint8_t* dst, int width, int height )
31 {
32 int n = width * height + 1;
33
34 while ( --n )
35 {
36 *dst++ = src[2];
37 *dst++ = src[1];
38 *dst++ = src[0];
39 *dst++ = src[3];
40 src += 4;
41 }
42 }
43
44 struct update_context {
45 f0r_instance_t frei0r;
46 int width;
47 int height;
48 double time;
49 uint32_t* inputs[2];
50 uint32_t* output;
51 void (*f0r_update) (f0r_instance_t instance, double time, const uint32_t* inframe, uint32_t* outframe);
52 void (*f0r_update2) (f0r_instance_t instance, double time, const uint32_t* inframe1,
53 const uint32_t* inframe2,const uint32_t* inframe3, uint32_t* outframe);
54 };
55
f0r_update_slice(int id,int index,int count,void * context)56 static int f0r_update_slice( int id, int index, int count, void *context )
57 {
58 struct update_context *ctx = context;
59 int slice_height = ctx->height / count;
60 int slice_offset = index * slice_height * ctx->width;
61 uint32_t *input = ctx->inputs[0] + slice_offset;
62 uint32_t *output = ctx->output + slice_offset;
63 ctx->f0r_update(ctx->frei0r, ctx->time, input, output);
64 return 0;
65 }
66
f0r_update2_slice(int id,int index,int count,void * context)67 static int f0r_update2_slice( int id, int index, int count, void *context )
68 {
69 struct update_context *ctx = context;
70 int slice_height = ctx->height / count;
71 int slice_offset = index * slice_height * ctx->width;
72 uint32_t *inputs[2] = {
73 ctx->inputs[0] + slice_offset,
74 ctx->inputs[1] + slice_offset
75 };
76 uint32_t *output = ctx->output + slice_offset;
77 ctx->f0r_update2(ctx->frei0r, ctx->time, inputs[0], inputs[1], NULL, output);
78 return 0;
79 }
80
process_frei0r_item(mlt_service service,mlt_position position,double time,int length,mlt_frame frame,uint8_t ** image,int * width,int * height)81 int process_frei0r_item( mlt_service service, mlt_position position, double time,
82 int length, mlt_frame frame, uint8_t **image, int *width, int *height )
83 {
84 int i=0;
85 mlt_properties prop = MLT_SERVICE_PROPERTIES(service);
86 f0r_instance_t (*f0r_construct) (unsigned int, unsigned int)
87 = mlt_properties_get_data(prop, "f0r_construct", NULL);
88 if (!f0r_construct) {
89 return -1;
90 }
91 void (*f0r_update) (f0r_instance_t instance, double time, const uint32_t* inframe, uint32_t* outframe)
92 = mlt_properties_get_data(prop, "f0r_update", NULL);
93 void (*f0r_get_plugin_info) (f0r_plugin_info_t*)
94 = mlt_properties_get_data(prop, "f0r_get_plugin_info", NULL);
95 void (*f0r_get_param_info) (f0r_param_info_t* info, int param_index)
96 = mlt_properties_get_data(prop, "f0r_get_param_info", NULL);
97 void (*f0r_set_param_value) (f0r_instance_t instance, f0r_param_t param, int param_index)
98 = mlt_properties_get_data(prop, "f0r_set_param_value", NULL);
99 void (*f0r_get_param_value) (f0r_instance_t instance, f0r_param_t param, int param_index)
100 = mlt_properties_get_data(prop, "f0r_get_param_value", NULL);
101 void (*f0r_update2) (f0r_instance_t instance, double time, const uint32_t* inframe1,
102 const uint32_t* inframe2, const uint32_t* inframe3, uint32_t* outframe)
103 = mlt_properties_get_data(prop, "f0r_update2", NULL);
104 mlt_service_type type = mlt_service_identify(service);
105 int not_thread_safe = mlt_properties_get_int(prop, "_not_thread_safe");
106 int slice_count = mlt_properties_get(prop, "threads") ? mlt_properties_get_int(prop, "threads") : -1;
107 const char *service_name = mlt_properties_get(prop, "mlt_service");
108 int is_cairoblend = service_name && !strcmp("frei0r.cairoblend", service_name);
109 double scale = mlt_profile_scale_width(mlt_service_profile(service), *width);
110 mlt_properties scale_map = mlt_properties_get_data(prop, "_resolution_scale", NULL);
111
112 // Determine the number of slices and slice height
113 if (slice_count == 0) {
114 slice_count = mlt_slices_count_normal();
115 } else {
116 slice_count = CLAMP(slice_count, 1, mlt_slices_count_normal());
117 }
118 // Reduce the slice count until the height is a multiple of slices
119 while (slice_count > 1 && (*height % slice_count)) {
120 --slice_count;
121 }
122 int slice_height = *height / slice_count;
123
124 // Use width and height in the frei0r instance key
125 char ctorname[1024] = "";
126 if (not_thread_safe)
127 sprintf(ctorname, "ctor-%dx%d", *width, slice_height);
128 else
129 #ifdef _WIN32
130 sprintf(ctorname, "ctor-%dx%d-%lu", *width, slice_height, GetCurrentThreadId());
131 #else
132 sprintf(ctorname, "ctor-%dx%d-%p", *width, slice_height, (void*) pthread_self());
133 #endif
134
135 mlt_service_lock(service);
136
137 f0r_instance_t inst = mlt_properties_get_data(prop, ctorname, NULL);
138 if (!inst) {
139 inst = f0r_construct(*width, slice_height);
140 mlt_properties_set_data(prop, ctorname, inst, 0, NULL, NULL);
141 }
142
143 if (!not_thread_safe && slice_count == 1)
144 mlt_service_unlock(service);
145
146 f0r_plugin_info_t info;
147 memset(&info, 0, sizeof(info));
148 if (f0r_get_plugin_info) {
149 f0r_get_plugin_info(&info);
150 for (i = 0; i < info.num_params; i++) {
151 prop = MLT_SERVICE_PROPERTIES(service);
152 f0r_param_info_t pinfo;
153 f0r_get_param_info(&pinfo,i);
154 char index[20];
155 snprintf( index, sizeof(index), "%d", i );
156 const char *name = index;
157 char *val = mlt_properties_get(prop, name);
158
159 // Special cairoblend handling for an override from the cairoblend_mode filter.
160 if (is_cairoblend && i == 1) {
161 if (mlt_properties_get(MLT_FRAME_PROPERTIES(frame), CAIROBLEND_MODE_PROPERTY)) {
162 name = CAIROBLEND_MODE_PROPERTY;
163 prop = MLT_FRAME_PROPERTIES(frame);
164 val = mlt_properties_get(prop, name);
165 } else if (!val && !mlt_properties_get(MLT_FRAME_PROPERTIES(frame), name)) {
166 // Reset plugin back to its default value.
167 char *default_val = "normal";
168 char *plugin_val = NULL;
169 f0r_get_param_value(inst, &plugin_val, i);
170 if (plugin_val && strcmp(default_val, plugin_val)) {
171 f0r_set_param_value(inst, &default_val, i);
172 continue;
173 }
174 }
175 }
176 if (!val) {
177 name = pinfo.name;
178 val = mlt_properties_get(prop, name);
179 }
180 if (!val) {
181 // Use the backwards-compatibility param name map.
182 mlt_properties map = mlt_properties_get_data(prop, "_param_name_map", NULL);
183 if (map) {
184 int j;
185 for (j = 0; !val && j < mlt_properties_count(map); j++) {
186 if (!strcmp(mlt_properties_get_value(map, j), index)) {
187 name = mlt_properties_get_name(map, j);
188 val = mlt_properties_get(prop, name);
189 }
190 }
191 }
192 }
193 if (val) {
194 switch (pinfo.type) {
195 case F0R_PARAM_DOUBLE:
196 case F0R_PARAM_BOOL:
197 {
198 double t = mlt_properties_anim_get_double(prop, name, position, length);
199 if (scale != 1.0) {
200 double scale2 = mlt_properties_get_double(scale_map, name);
201 if (scale2 != 0.0)
202 t *= scale * scale2;
203 }
204 f0r_set_param_value(inst,&t,i);
205 break;
206 }
207 case F0R_PARAM_COLOR:
208 {
209 f0r_param_color_t f_color;
210 mlt_color m_color = mlt_properties_get(prop, index) ?
211 mlt_properties_get_color(prop, index) : mlt_properties_get_color(prop, pinfo.name);
212 f_color.r = (float) m_color.r / 255.0f;
213 f_color.g = (float) m_color.g / 255.0f;
214 f_color.b = (float) m_color.b / 255.0f;
215 f0r_set_param_value(inst, &f_color, i);
216 break;
217 }
218 case F0R_PARAM_STRING:
219 {
220 val = mlt_properties_anim_get(prop, name, position, length);
221 f0r_set_param_value(inst, &val, i);
222 break;
223 }
224 }
225 }
226 }
227 }
228
229 int video_area = *width * *height;
230 uint32_t *result = mlt_pool_alloc(video_area * sizeof(uint32_t));
231 uint32_t *extra = NULL;
232 uint32_t *source[2] = { (uint32_t*) image[0], (uint32_t*) image[1] };
233 uint32_t *dest = result;
234
235 if (info.color_model == F0R_COLOR_MODEL_BGRA8888) {
236 if (type == producer_type) {
237 dest = source[0];
238 } else {
239 rgba_bgra(image[0], (uint8_t*) result, *width, *height);
240 source[0] = result;
241 dest = (uint32_t*) image[0];
242 if (type == transition_type && f0r_update2) {
243 extra = mlt_pool_alloc(video_area * sizeof(uint32_t));
244 rgba_bgra(image[1], (uint8_t*) extra, *width, *height);
245 source[1] = extra;
246 }
247 }
248 }
249 if (type == producer_type) {
250 if (slice_count > 0) {
251 struct update_context ctx = {
252 .frei0r = inst,
253 .width = *width,
254 .height = *height,
255 .time = time,
256 .inputs = { NULL, NULL },
257 .output = dest,
258 .f0r_update = f0r_update
259 };
260 mlt_slices_run_normal(slice_count, f0r_update_slice, &ctx);
261 } else {
262 f0r_update(inst, time, NULL, dest);
263 }
264 } else if (type == filter_type) {
265 if (slice_count > 0) {
266 struct update_context ctx = {
267 .frei0r = inst,
268 .width = *width,
269 .height = *height,
270 .time = time,
271 .inputs = { source[0], NULL },
272 .output = dest,
273 .f0r_update = f0r_update
274 };
275 mlt_slices_run_normal(slice_count, f0r_update_slice, &ctx);
276 } else {
277 f0r_update(inst, time, source[0], dest);
278 }
279 } else if (type == transition_type && f0r_update2) {
280 if (slice_count > 0) {
281 struct update_context ctx = {
282 .frei0r = inst,
283 .width = *width,
284 .height = *height,
285 .time = time,
286 .inputs = { source[0], source[1] },
287 .output = dest,
288 .f0r_update2 = f0r_update2
289 };
290 mlt_slices_run_normal(slice_count, f0r_update2_slice, &ctx);
291 } else {
292 f0r_update2(inst, time, source[0], source[1], NULL, dest);
293 }
294 }
295 if (not_thread_safe || slice_count != 1)
296 mlt_service_unlock(service);
297 if (info.color_model == F0R_COLOR_MODEL_BGRA8888) {
298 rgba_bgra((uint8_t*) dest, (uint8_t*) result, *width, *height);
299 }
300 *image = (uint8_t*) result;
301 mlt_frame_set_image(frame, (uint8_t*) result, video_area * sizeof(uint32_t), mlt_pool_release);
302 if (extra)
303 mlt_pool_release(extra);
304
305 return 0;
306 }
307
destruct(mlt_properties prop)308 void destruct (mlt_properties prop ) {
309
310 void (*f0r_destruct) (f0r_instance_t instance) = mlt_properties_get_data(prop, "f0r_destruct", NULL);
311 void (*f0r_deinit) (void) = mlt_properties_get_data(prop, "f0r_deinit", NULL);
312 int i = 0;
313
314 if (f0r_deinit)
315 f0r_deinit();
316
317 for (i=0; i < mlt_properties_count(prop); i++) {
318 if (strstr(mlt_properties_get_name(prop, i), "ctor-")) {
319 void * inst = mlt_properties_get_data(prop, mlt_properties_get_name(prop, i), NULL);
320 if (inst) {
321 f0r_destruct((f0r_instance_t) inst);
322 }
323 }
324 }
325 void (*dlclose) (void*) = mlt_properties_get_data(prop, "_dlclose", NULL);
326 void *handle = mlt_properties_get_data(prop, "_dlclose_handle", NULL);
327
328 if (handle && dlclose)
329 dlclose(handle);
330 }
331