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