1 /*
2  *			GPAC - Multimedia Framework C SDK
3  *
4  *			Authors: Jean Le Feuvre
5  *			Copyright (c) Telecom ParisTech 2018
6  *					All rights reserved
7  *
8  *  This file is part of GPAC / video cropping filter
9  *
10  *  GPAC is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU Lesser General Public License as published by
12  *  the Free Software Foundation; either version 2, or (at your option)
13  *  any later version.
14  *
15  *  GPAC 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 Lesser General Public License for more details.
19  *
20  *  You should have received a copy of the GNU Lesser General Public
21  *  License along with this library; see the file COPYING.  If not, write to
22  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23  *
24  */
25 
26 #include <gpac/filters.h>
27 #include <gpac/list.h>
28 #include <gpac/constants.h>
29 #include <gpac/network.h>
30 
31 typedef struct
32 {
33 	//options
34 	const char *wnd;
35 	Bool copy;
36 	u32 round;
37 
38 	//internal data
39 	Bool initialized;
40 
41 	GF_FilterPid *ipid, *opid;
42 	u32 w, h, stride, s_pfmt;
43 	GF_Fraction ar;
44 	Bool passthrough;
45 
46 	u32 dst_stride[5];
47 	u32 src_stride[5];
48 	u32 nb_planes, nb_src_planes, out_size, out_src_size, src_uv_height, dst_uv_height;
49 
50 	Bool use_reference;
51 	u32 dst_width, dst_height;
52 	s32 src_x, src_y;
53 	Bool packed_422;
54 
55 	GF_List *frames, *frames_res;
56 } GF_VCropCtx;
57 
58 typedef struct
59 {
60 	GF_FilterFrameInterface frame_ifce;
61 
62 	//reference to the source packet
63 	GF_FilterPacket *pck;
64 	u8 *planes[5];
65 	u32 stride[5];
66 
67 	GF_VCropCtx *ctx;
68 } GF_VCropFrame;
69 
70 
vcrop_frame_get_plane(GF_FilterFrameInterface * frame,u32 plane_idx,const u8 ** outPlane,u32 * outStride)71 GF_Err vcrop_frame_get_plane(GF_FilterFrameInterface *frame, u32 plane_idx, const u8 **outPlane, u32 *outStride)
72 {
73 	GF_VCropFrame *vframe = frame->user_data;
74 
75 	if (plane_idx>=vframe->ctx->nb_planes) return GF_BAD_PARAM;
76 	if (outPlane) *outPlane = vframe->planes[plane_idx];
77 	if (outStride) *outStride = vframe->stride[plane_idx];
78 	return GF_OK;
79 }
80 
vcrop_packet_destruct(GF_Filter * filter,GF_FilterPid * pid,GF_FilterPacket * pck)81 void vcrop_packet_destruct(GF_Filter *filter, GF_FilterPid *pid, GF_FilterPacket *pck)
82 {
83 	GF_VCropFrame *vframe;
84 	GF_FilterFrameInterface *frame_ifce = gf_filter_pck_get_frame_interface(pck);
85 	if (!frame_ifce) return;
86 	vframe = frame_ifce->user_data;
87 	assert(vframe->pck);
88 	gf_filter_pck_unref(vframe->pck);
89 	vframe->pck = NULL;
90 	gf_list_add(vframe->ctx->frames_res, vframe);
91 }
92 
93 
vcrop_process(GF_Filter * filter)94 static GF_Err vcrop_process(GF_Filter *filter)
95 {
96 	const u8 *data;
97 	u8 *output;
98 	u32 size;
99 	u32 bps;
100 	u32 s_off_x, s_off_y, d_off_x, d_off_y, i, copy_w, copy_h;
101 	u8 *src, *dst;
102 	u8 *src_planes[5];
103 	u8 *dst_planes[5];
104 	Bool do_memset = GF_FALSE;
105 	GF_FilterPacket *dst_pck;
106 	GF_FilterFrameInterface *frame_ifce;
107 	GF_VCropCtx *ctx = gf_filter_get_udta(filter);
108 	GF_FilterPacket *pck = gf_filter_pid_get_packet(ctx->ipid);
109 
110 	if (!pck) {
111 		if (gf_filter_pid_is_eos(ctx->ipid)) {
112 			gf_filter_pid_set_eos(ctx->opid);
113 			return GF_EOS;
114 		}
115 		return GF_OK;
116 	}
117 
118 	if (ctx->passthrough) {
119 		gf_filter_pck_forward(pck, ctx->opid);
120 		gf_filter_pid_drop_packet(ctx->ipid);
121 		return GF_OK;
122 	}
123 	data = gf_filter_pck_get_data(pck, &size);
124 	frame_ifce = gf_filter_pck_get_frame_interface(pck);
125 
126 	memset(src_planes, 0, sizeof(src_planes));
127 	memset(dst_planes, 0, sizeof(dst_planes));
128 
129 	if (data) {
130 		src_planes[0] = (u8 *) data;
131 
132 		if (ctx->nb_src_planes==1) {
133 		} else if (ctx->nb_src_planes==2) {
134 			src_planes[1] = src_planes[0] + ctx->src_stride[0]*ctx->h;
135 		} else if (ctx->nb_src_planes==3) {
136 			src_planes[1] = src_planes[0] + ctx->src_stride[0] * ctx->h;
137 			src_planes[2] = src_planes[1] + ctx->src_stride[1] * ctx->src_uv_height;
138 		} else if (ctx->nb_src_planes==4) {
139 			src_planes[1] = src_planes[0] + ctx->src_stride[0] * ctx->h;
140 			src_planes[2] = src_planes[1] + ctx->src_stride[1] * ctx->src_uv_height;
141 			src_planes[3] = src_planes[2] + ctx->src_stride[2] * ctx->src_uv_height;
142 		}
143 	} else if (frame_ifce && frame_ifce->get_plane) {
144 		for (i=0; i<4; i++) {
145 			if (frame_ifce->get_plane(frame_ifce, i, (const u8 **) &src_planes[i], &ctx->src_stride[i])!=GF_OK)
146 				break;
147 		}
148 	} else {
149 		GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[VCrop] No data associated with packet, not supported\n"));
150 		gf_filter_pid_drop_packet(ctx->ipid);
151 		return GF_NOT_SUPPORTED;
152 	}
153 
154 	bps = gf_pixel_get_bytes_per_pixel(ctx->s_pfmt);
155 
156 	copy_w = ctx->dst_width;
157 	copy_h = ctx->dst_height;
158 	s_off_x = s_off_y = d_off_x = d_off_y = 0;
159 	if (ctx->src_x>=0) {
160 		s_off_x = ctx->src_x;
161 	} else {
162 		d_off_x = (u32) (-ctx->src_x);
163 		copy_w = (s32) ctx->dst_width + ctx->src_x;
164 		do_memset = GF_TRUE;
165 	}
166 	if (s_off_x + copy_w > ctx->w) {
167 		copy_w = ctx->w - s_off_x;
168 		do_memset = GF_TRUE;
169 	}
170 	if (ctx->src_y>=0) {
171 		s_off_y = ctx->src_y;
172 	} else {
173 		d_off_y = (u32) (-ctx->src_y);
174 		copy_h = (s32) ctx->dst_height + ctx->src_y;
175 		do_memset = GF_TRUE;
176 	}
177 	if (s_off_y + copy_h > ctx->h) {
178 		copy_h = ctx->h - s_off_y;
179 		do_memset = GF_TRUE;
180 	}
181 
182 	if (ctx->use_reference) {
183 		GF_VCropFrame *vframe = gf_list_pop_back(ctx->frames_res);
184 		if (!vframe) {
185 			GF_SAFEALLOC(vframe, GF_VCropFrame);
186 			if (!vframe) return GF_OUT_OF_MEM;
187 		}
188 		vframe->ctx = ctx;
189 		memcpy(vframe->stride, ctx->src_stride, sizeof(vframe->stride));
190 		if (ctx->packed_422) {
191 			vframe->planes[0] = src_planes[0] + s_off_x * bps * 2 + ctx->src_stride[0] * s_off_y;
192 		} else {
193 			vframe->planes[0] = src_planes[0] + s_off_x * bps + ctx->src_stride[0] * s_off_y;
194 		}
195 		//nv12/21
196 		if (ctx->nb_planes==2) {
197 			vframe->planes[1] = src_planes[1] + s_off_x * bps + ctx->src_stride[1] * s_off_y/2;
198 		} else if (ctx->nb_planes>=3) {
199 			u32 div_x, div_y;
200 			//alpha/depth/other plane, treat as luma plane
201 			if (ctx->nb_planes==4) {
202 				vframe->planes[3] = src_planes[3] + s_off_x * bps + ctx->src_stride[3] * s_off_y;
203 			}
204 			div_x = (ctx->src_stride[1]==ctx->src_stride[0]) ? 1 : 2;
205 			div_y = (ctx->src_uv_height==ctx->h) ? 1 : 2;
206 
207 			vframe->planes[1] = src_planes[1] + s_off_x * bps / div_x + ctx->src_stride[1] * s_off_y / div_y;
208 			vframe->planes[2] =src_planes[2] + s_off_x * bps / div_x + ctx->src_stride[2] * s_off_y / div_y;
209 		}
210 		vframe->frame_ifce.user_data = vframe;
211 		vframe->frame_ifce.get_plane = vcrop_frame_get_plane;
212 		dst_pck = gf_filter_pck_new_frame_interface(ctx->opid, &vframe->frame_ifce, vcrop_packet_destruct);
213 		//keep a ref to input packet
214 		vframe->pck = pck;
215 		gf_filter_pck_ref(&vframe->pck);
216 		gf_filter_pck_merge_properties(pck, dst_pck);
217 		gf_filter_pck_send(dst_pck);
218 		//remove input packet
219 		gf_filter_pid_drop_packet(ctx->ipid);
220 		return GF_OK;
221 	}
222 
223 
224 	dst_pck = gf_filter_pck_new_alloc(ctx->opid, ctx->out_size, &output);
225 	if (!dst_pck) {
226 		gf_filter_pid_drop_packet(ctx->ipid);
227 		return GF_OUT_OF_MEM;
228 	}
229 	gf_filter_pck_merge_properties(pck, dst_pck);
230 	dst_planes[0] = output;
231 	if (ctx->nb_planes==1) {
232 	} else if (ctx->nb_planes==2) {
233 		dst_planes[1] = output + ctx->dst_stride[0] * ctx->dst_height;
234 	} else if (ctx->nb_planes==3) {
235 		dst_planes[1] = output + ctx->dst_stride[0] * ctx->dst_height;
236 		dst_planes[2] = dst_planes[1] + ctx->dst_stride[1]*ctx->dst_uv_height;
237 	} else if (ctx->nb_planes==4) {
238 		dst_planes[1] = output + ctx->dst_stride[0] * ctx->dst_height;
239 		dst_planes[2] = dst_planes[1] + ctx->dst_stride[1]*ctx->dst_uv_height;
240 		dst_planes[3] = dst_planes[2] + ctx->dst_stride[2]*ctx->dst_uv_height;
241 	} else {
242 		gf_filter_pid_drop_packet(ctx->ipid);
243 		return GF_NOT_SUPPORTED;
244 	}
245 
246 	if (do_memset) {
247 		memset(output, 0x00, sizeof(char)*ctx->out_size);
248 	}
249 
250 	//YUYV variations need *2 on horizontal dimension
251 	if (ctx->packed_422) {
252 		src = src_planes[0] + s_off_x * bps * 2 + ctx->src_stride[0] * s_off_y;
253 		dst = dst_planes[0] + d_off_x * bps * 2 + ctx->dst_stride[0] * d_off_y;
254 		for (i=0; i<copy_h; i++) {
255 			memcpy(dst, src, bps * copy_w * 2);
256 			src += ctx->src_stride[0];
257 			dst += ctx->dst_stride[0];
258 		}
259 	} else {
260 		//copy first plane
261 		src = src_planes[0] + s_off_x * bps + ctx->src_stride[0] * s_off_y;
262 		dst = dst_planes[0] + d_off_x * bps + ctx->dst_stride[0] * d_off_y;
263 		for (i=0; i<copy_h; i++) {
264 			memcpy(dst, src, bps * copy_w);
265 			src += ctx->src_stride[0];
266 			dst += ctx->dst_stride[0];
267 		}
268 	}
269 
270 	//nv12/21
271 	if (ctx->nb_planes==2) {
272 		src = src_planes[1] + s_off_x * bps + ctx->src_stride[1] * s_off_y/2;
273 		dst = dst_planes[1] + d_off_x * bps + ctx->dst_stride[1] * d_off_y/2;
274 		//half vertical res (/2)
275 		for (i=0; i<copy_h/2; i++) {
276 			//half horizontal res (/2) but two chroma packed per pixel (*2)
277 			memcpy(dst, src, bps * copy_w);
278 			src += ctx->src_stride[1];
279 			dst += ctx->dst_stride[1];
280 		}
281 	} else if ((ctx->nb_planes==3) || (ctx->nb_planes==4)) {
282 		u32 div_x, div_y;
283 		//alpha/depth/other plane, treat as luma plane
284 		if (ctx->nb_planes==4) {
285 			src = src_planes[3] + s_off_x * bps + ctx->src_stride[3] * s_off_y;
286 			dst = dst_planes[3] + d_off_x * bps + ctx->dst_stride[3] * d_off_y;
287 			for (i=0; i<copy_h; i++) {
288 				memcpy(dst, src, bps * copy_w);
289 				src += ctx->src_stride[3];
290 				dst += ctx->dst_stride[3];
291 			}
292 		}
293 
294 		div_x = (ctx->src_stride[1]==ctx->src_stride[0]) ? 1 : 2;
295 		div_y = (ctx->src_uv_height==ctx->h) ? 1 : 2;
296 
297 		src = src_planes[1] + s_off_x * bps / div_x + ctx->src_stride[1] * s_off_y / div_y;
298 		dst = dst_planes[1] + d_off_x * bps / div_x + ctx->dst_stride[1] * d_off_y / div_y;
299 		copy_h /= div_y;
300 		copy_w /= div_x;
301 
302 		for (i=0; i<copy_h; i++) {
303 			memcpy(dst, src, bps * copy_w);
304 			src += ctx->src_stride[1];
305 			dst += ctx->dst_stride[1];
306 		}
307 
308 		src = src_planes[2] + s_off_x * bps / div_x + ctx->src_stride[2] * s_off_y / div_y;
309 		dst = dst_planes[2] + d_off_x * bps / div_x + ctx->dst_stride[2] * d_off_y / div_y;
310 		for (i=0; i<copy_h; i++) {
311 			memcpy(dst, src, bps * copy_w);
312 			src += ctx->src_stride[1];
313 			dst += ctx->dst_stride[1];
314 		}
315 	}
316 
317 	gf_filter_pck_send(dst_pck);
318 	gf_filter_pid_drop_packet(ctx->ipid);
319 	return GF_OK;
320 }
321 
parse_crop_props(GF_VCropCtx * ctx,u32 src_w,u32 src_h,GF_PixelFormat pfmt)322 static Bool parse_crop_props(GF_VCropCtx *ctx, u32 src_w, u32 src_h, GF_PixelFormat pfmt)
323 {
324 	s32 dim[4];
325 	s32 dim_off[4];
326 	Bool dim_pc[4];
327 	u32 nb_dim=0;
328 	char *args = (char *)ctx->wnd;
329 
330 	memset(dim, 0, sizeof(dim));
331 	memset(dim_pc, 0, sizeof(dim_pc));
332 	memset(dim_off, 0, sizeof(dim_off));
333 
334 	while (args) {
335 		char *sep_offset=NULL, *sep_pc, sign=0;
336 		char *sep = strchr(args, 'x');
337 		if (sep) sep[0] = 0;
338 
339 		sep_pc = strchr(args, '%');
340 		if (sep_pc) {
341 			sep_pc[0] = 0;
342 			dim[nb_dim] = atoi(args);
343 			sep_pc[0] = '%';
344 			dim_pc[nb_dim] = GF_TRUE;
345 
346 			sep_offset = strchr(sep_pc, '+');
347 			if (sep_offset) {
348 				sign = sep_offset[0];
349 				dim_off[nb_dim] = atoi(sep_offset+1);
350 				sep_offset[0] = 0;
351 			} else {
352 				sep_offset = strchr(args, '-');
353 				if (sep_offset) {
354 					sign = sep_offset[0];
355 					dim_off[nb_dim] = - atoi(sep_offset+1);
356 					sep_offset[0] = 0;
357 				}
358 			}
359 
360 		} else {
361 			dim[nb_dim] = atoi(args);
362 		}
363 
364 		if (sep_offset) sep_offset[0] = sign;
365 		nb_dim++;
366 		if (!sep) break;
367 		sep[0] = 'x';
368 		args = sep+1;
369 		if (nb_dim==4) break;
370 	}
371 
372 #define GET_DIM(_dst, _i, _s) if (dim_pc[_i]) \
373 								_dst = (dim[_i] * (s32) _s / 100) + dim_off[_i]; \
374 							else \
375 								_dst = dim[_i] + dim_off[_i]; \
376 
377 
378 	if (nb_dim==2) {
379 		ctx->src_x = 0;
380 		ctx->src_y = 0;
381 		GET_DIM(ctx->dst_width, 0, src_w)
382 		GET_DIM(ctx->dst_height, 1, src_h)
383 	} else if (nb_dim==4) {
384 		GET_DIM(ctx->src_x, 0, src_w)
385 		GET_DIM(ctx->src_y, 1, src_h)
386 		GET_DIM(ctx->dst_width, 2, src_w)
387 		GET_DIM(ctx->dst_height, 3, src_h)
388 	} else {
389 		GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[VCrop] Cannot parse crop parameters, expected 2 or 4 numbers, got %d\n", nb_dim));
390 		return GF_FALSE;
391 	}
392 	if ((s32) ctx->dst_width < 0) {
393 		GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[VCrop] Negative destination width %d !\n", (s32) ctx->dst_width));
394 		return GF_FALSE;
395 	}
396 	if ((s32) ctx->dst_height < 0) {
397 		GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[VCrop] Negative destination height %d !\n", (s32) ctx->dst_height));
398 		return GF_FALSE;
399 	}
400 
401 #define ROUND_IT(_a) { if ((ctx->round==0) || (ctx->round==2)) { (_a)++; } else { (_a)--; } }
402 
403 	//for YUV 420, adjust to multiple of 2 on both dim
404 	ctx->packed_422 = GF_FALSE;
405 	switch (pfmt) {
406 	case GF_PIXEL_YUV:
407 	case GF_PIXEL_NV21:
408 	case GF_PIXEL_NV21_10:
409 	case GF_PIXEL_NV12:
410 	case GF_PIXEL_NV12_10:
411 	case GF_PIXEL_YUVA:
412 	case GF_PIXEL_YUVD:
413 	case GF_PIXEL_YUV_10:
414 		if (ctx->src_x % 2) ROUND_IT(ctx->src_x);
415 		if (ctx->src_y % 2) ROUND_IT(ctx->src_y);
416 		if ((ctx->src_x + ctx->dst_width) % 2) ROUND_IT(ctx->dst_width);
417 		if ((ctx->src_y + ctx->dst_height) % 2) ROUND_IT(ctx->dst_height);
418 		break;
419 	//for YUV 422, adjust to multiple of 2 on horizontal dim
420 	case GF_PIXEL_YUYV:
421 	case GF_PIXEL_YVYU:
422 	case GF_PIXEL_UYVY:
423 	case GF_PIXEL_VYUY:
424 		ctx->packed_422 = GF_TRUE;
425 	case GF_PIXEL_YUV422:
426 	case GF_PIXEL_YUV422_10:
427 		if (ctx->src_x % 2) ROUND_IT(ctx->src_x);
428 		if ((ctx->src_x + ctx->dst_width) % 2) ROUND_IT(ctx->dst_width);
429 		if (ctx->round>1) {
430 			if (ctx->src_y % 2) ROUND_IT(ctx->src_y);
431 			if ((ctx->src_y + ctx->dst_height) % 2) ROUND_IT(ctx->dst_height);
432 		}
433 		break;
434 	default:
435 		if (ctx->round>1) {
436 			if (ctx->src_x % 2) ROUND_IT(ctx->src_x);
437 			if (ctx->src_y % 2) ROUND_IT(ctx->src_y);
438 			if ((ctx->src_x + ctx->dst_width) % 2) ROUND_IT(ctx->dst_width);
439 			if ((ctx->src_y + ctx->dst_height) % 2) ROUND_IT(ctx->dst_height);
440 		}
441 		break;
442 	}
443 	return GF_TRUE;
444 }
445 
vcrop_configure_pid(GF_Filter * filter,GF_FilterPid * pid,Bool is_remove)446 static GF_Err vcrop_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
447 {
448 	GF_PropVec2i vec;
449 	const GF_PropertyValue *p;
450 	u32 w, h, stride, pfmt;
451 	GF_Fraction sar;
452 	GF_VCropCtx *ctx = gf_filter_get_udta(filter);
453 
454 	if (is_remove) {
455 		if (ctx->opid) {
456 			gf_filter_pid_remove(ctx->opid);
457 		}
458 		return GF_OK;
459 	}
460 	if (! gf_filter_pid_check_caps(pid))
461 		return GF_NOT_SUPPORTED;
462 
463 	if (!ctx->opid) {
464 		ctx->opid = gf_filter_pid_new(filter);
465 	}
466 	//copy properties at init or reconfig
467 	gf_filter_pid_copy_properties(ctx->opid, pid);
468 
469 	if (!ctx->ipid) {
470 		ctx->ipid = pid;
471 	}
472 	w = h = pfmt = stride = 0;
473 	p = gf_filter_pid_get_property(pid, GF_PROP_PID_WIDTH);
474 	if (p) w = p->value.uint;
475 	p = gf_filter_pid_get_property(pid, GF_PROP_PID_HEIGHT);
476 	if (p) h = p->value.uint;
477 	p = gf_filter_pid_get_property(pid, GF_PROP_PID_STRIDE);
478 	if (p) stride = p->value.uint;
479 	p = gf_filter_pid_get_property(pid, GF_PROP_PID_PIXFMT);
480 	if (p) pfmt = p->value.uint;
481 	p = gf_filter_pid_get_property(pid, GF_PROP_PID_SAR);
482 	if (p) sar = p->value.frac;
483 	else sar.den = sar.num = 1;
484 
485 	if (!w || !h || !pfmt) {
486 		ctx->passthrough = GF_TRUE;
487 		return GF_OK;
488 	}
489 	ctx->passthrough = GF_FALSE;
490 
491 	if ((ctx->w == w) && (ctx->h == h) && (ctx->s_pfmt == pfmt) && (ctx->stride == stride)) {
492 		//nothing to reconfigure
493 	} else {
494 		Bool res;
495 		if (! parse_crop_props(ctx, w, h, pfmt)) {
496 			return GF_BAD_PARAM;
497 		}
498 
499 		if (ctx->src_x<0) ctx->use_reference = GF_FALSE;
500 		else if (ctx->src_y<0) ctx->use_reference = GF_FALSE;
501 		else if (ctx->src_x + ctx->dst_width > w) ctx->use_reference = GF_FALSE;
502 		else if (ctx->src_y + ctx->dst_height > h) ctx->use_reference = GF_FALSE;
503 		else ctx->use_reference = ctx->copy ? GF_FALSE : GF_TRUE;
504 
505 		if (ctx->use_reference) {
506 			if (!ctx->frames) ctx->frames = gf_list_new();
507 			if (!ctx->frames_res) ctx->frames_res = gf_list_new();
508 		}
509 
510 		//get layout info for source
511 		memset(ctx->src_stride, 0, sizeof(ctx->src_stride));
512 		if (ctx->stride) ctx->src_stride[0] = ctx->stride;
513 
514 		res = gf_pixel_get_size_info(pfmt, w, h, &ctx->out_src_size, &ctx->src_stride[0], &ctx->src_stride[1], &ctx->nb_src_planes, &ctx->src_uv_height);
515 		if (!res) {
516 			GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[VCrop] Failed to query source pixel format characteristics\n"));
517 			return GF_NOT_SUPPORTED;
518 		}
519 		if (ctx->nb_src_planes==3) ctx->src_stride[2] = ctx->src_stride[1];
520 		if (ctx->nb_src_planes==4) ctx->src_stride[3] = ctx->src_stride[0];
521 
522 
523 		//get layout info for dest
524 		memset(ctx->dst_stride, 0, sizeof(ctx->dst_stride));
525 		res = gf_pixel_get_size_info(pfmt, ctx->dst_width, ctx->dst_height, &ctx->out_size, &ctx->dst_stride[0], &ctx->dst_stride[1], &ctx->nb_planes, &ctx->dst_uv_height);
526 		if (!res) {
527 			GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[VCrop] Failed to query output pixel format characteristics\n"));
528 			return GF_NOT_SUPPORTED;
529 		}
530 		if (ctx->nb_planes==3) ctx->dst_stride[2] = ctx->dst_stride[1];
531 		if (ctx->nb_planes==4) ctx->dst_stride[3] = ctx->dst_stride[0];
532 
533 
534 		ctx->w = w;
535 		ctx->h = h;
536 		ctx->s_pfmt = pfmt;
537 
538 		GF_LOG(GF_LOG_INFO, GF_LOG_MEDIA, ("[VCrop] Configured output window to crop %dx%dx%dx%d from full frame size %dx%d\n", ctx->src_x, ctx->src_y, ctx->dst_width, ctx->dst_height, ctx->w, ctx->h));
539 
540 		if (!ctx->src_x && !ctx->src_y && (ctx->dst_width==ctx->w) &&  (ctx->dst_height==ctx->h) ) {
541 			ctx->passthrough = GF_TRUE;
542 		}
543 	}
544 
545 	gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_WIDTH, &PROP_UINT(ctx->dst_width));
546 	gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_HEIGHT, &PROP_UINT(ctx->dst_height));
547 	if (ctx->use_reference) {
548 		gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STRIDE, &PROP_UINT(ctx->src_stride[0] ));
549 		if (ctx->nb_planes>1)
550 			gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STRIDE_UV, &PROP_UINT(ctx->src_stride[1]));
551 	} else {
552 		gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STRIDE, &PROP_UINT(ctx->dst_stride[0] ));
553 		if (ctx->nb_planes>1)
554 			gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STRIDE_UV, &PROP_UINT(ctx->dst_stride[1]));
555 	}
556 	gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SAR, &PROP_FRAC(sar) );
557 	vec.x = ctx->src_x;
558 	vec.y = ctx->src_y;
559 	gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CROP_POS, &PROP_VEC2I(vec) );
560 	vec.x = ctx->w;
561 	vec.y = ctx->h;
562 	gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_ORIG_SIZE, &PROP_VEC2I(vec) );
563 
564 	return GF_OK;
565 }
566 
vcrop_finalize(GF_Filter * filter)567 void vcrop_finalize(GF_Filter *filter)
568 {
569 	GF_VCropCtx *ctx = gf_filter_get_udta(filter);
570 	if (ctx->frames) {
571 		while (gf_list_count(ctx->frames)) {
572 			GF_VCropFrame *vframe = gf_list_pop_back(ctx->frames);
573 			gf_free(vframe);
574 		}
575 		gf_list_del(ctx->frames);
576 	}
577 	if (ctx->frames_res) {
578 		while (gf_list_count(ctx->frames_res)) {
579 			GF_VCropFrame *vframe = gf_list_pop_back(ctx->frames_res);
580 			gf_free(vframe);
581 		}
582 		gf_list_del(ctx->frames_res);
583 	}
584 }
585 
586 
587 #define OFFS(_n)	#_n, offsetof(GF_VCropCtx, _n)
588 static GF_FilterArgs VCropArgs[] =
589 {
590 	{ OFFS(wnd), "size of output to crop, indicated as TxLxWxH. If % is indicated after a number, the value is in percent of the source width (for L and W) or height (for T and H). An absolute offset (+x, -x) can be added after percent", GF_PROP_STRING, NULL, NULL, 0},
591 	{ OFFS(copy), "copy the source pixels. By default the filter will try to forward crop frames by adjusting offsets and strides of the source if possible (window contained in frame)", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
592 	{ OFFS(round), "adjust dimension to be a multiple of 2.\n"
593 	"- up: up rounding\n"
594 	"- down: down rounding\n"
595 	"- allup: up rounding on formats that do not require it (RGB, YUV444)\n"
596 	"- alldown: down rounding on formats that do not require it (RGB, YUV444)", GF_PROP_UINT, "up", "up|down|allup|alldown", GF_FS_ARG_HINT_ADVANCED},
597 	{0}
598 };
599 
600 static const GF_FilterCapability VCropCaps[] =
601 {
602 	CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
603 	CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_RAW),
604 	CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
605 	CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_RAW),
606 };
607 
608 GF_FilterRegister VCropRegister = {
609 	.name = "vcrop",
610 	GF_FS_SET_DESCRIPTION("Video crop")
611 	GF_FS_SET_HELP("This filter is used to crop raw video data.")
612 	.private_size = sizeof(GF_VCropCtx),
613 	.flags = GF_FS_REG_EXPLICIT_ONLY,
614 	.args = VCropArgs,
615 	.configure_pid = vcrop_configure_pid,
616 	SETCAPS(VCropCaps),
617 	.process = vcrop_process,
618 	.finalize = vcrop_finalize,
619 };
620 
621 
622 
vcrop_register(GF_FilterSession * session)623 const GF_FilterRegister *vcrop_register(GF_FilterSession *session)
624 {
625 	return &VCropRegister;
626 }
627