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