Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2018-2021
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 :
71 1446 : GF_Err vcrop_frame_get_plane(GF_FilterFrameInterface *frame, u32 plane_idx, const u8 **outPlane, u32 *outStride)
72 : {
73 1446 : GF_VCropFrame *vframe = frame->user_data;
74 :
75 1446 : if (plane_idx>=vframe->ctx->nb_planes) return GF_BAD_PARAM;
76 1446 : if (outPlane) *outPlane = vframe->planes[plane_idx];
77 1446 : if (outStride) *outStride = vframe->stride[plane_idx];
78 : return GF_OK;
79 : }
80 :
81 698 : void vcrop_packet_destruct(GF_Filter *filter, GF_FilterPid *pid, GF_FilterPacket *pck)
82 : {
83 : GF_VCropFrame *vframe;
84 698 : GF_FilterFrameInterface *frame_ifce = gf_filter_pck_get_frame_interface(pck);
85 698 : if (!frame_ifce) return;
86 698 : vframe = frame_ifce->user_data;
87 : assert(vframe->pck);
88 698 : gf_filter_pck_unref(vframe->pck);
89 698 : vframe->pck = NULL;
90 698 : gf_list_add(vframe->ctx->frames_res, vframe);
91 : }
92 :
93 :
94 1745 : 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 1745 : GF_VCropCtx *ctx = gf_filter_get_udta(filter);
108 1745 : GF_FilterPacket *pck = gf_filter_pid_get_packet(ctx->ipid);
109 :
110 1745 : if (!pck) {
111 534 : if (gf_filter_pid_is_eos(ctx->ipid)) {
112 40 : gf_filter_pid_set_eos(ctx->opid);
113 40 : return GF_EOS;
114 : }
115 : return GF_OK;
116 : }
117 :
118 1211 : if (ctx->passthrough) {
119 0 : gf_filter_pck_forward(pck, ctx->opid);
120 0 : gf_filter_pid_drop_packet(ctx->ipid);
121 0 : return GF_OK;
122 : }
123 1211 : data = gf_filter_pck_get_data(pck, &size);
124 1211 : 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 1211 : if (data) {
130 1211 : src_planes[0] = (u8 *) data;
131 :
132 1211 : if (ctx->nb_src_planes==1) {
133 617 : } else if (ctx->nb_src_planes==2) {
134 108 : src_planes[1] = src_planes[0] + ctx->src_stride[0]*ctx->h;
135 509 : } else if (ctx->nb_src_planes==3) {
136 509 : src_planes[1] = src_planes[0] + ctx->src_stride[0] * ctx->h;
137 509 : src_planes[2] = src_planes[1] + ctx->src_stride[1] * ctx->src_uv_height;
138 0 : } else if (ctx->nb_src_planes==4) {
139 0 : src_planes[1] = src_planes[0] + ctx->src_stride[0] * ctx->h;
140 0 : src_planes[2] = src_planes[1] + ctx->src_stride[1] * ctx->src_uv_height;
141 0 : src_planes[3] = src_planes[2] + ctx->src_stride[2] * ctx->src_uv_height;
142 : }
143 0 : } else if (frame_ifce && frame_ifce->get_plane) {
144 0 : for (i=0; i<4; i++) {
145 0 : 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 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[VCrop] No data associated with packet, not supported\n"));
150 0 : gf_filter_pid_drop_packet(ctx->ipid);
151 0 : return GF_NOT_SUPPORTED;
152 : }
153 :
154 1211 : bps = gf_pixel_get_bytes_per_pixel(ctx->s_pfmt);
155 :
156 1211 : copy_w = ctx->dst_width;
157 1211 : copy_h = ctx->dst_height;
158 : s_off_x = s_off_y = d_off_x = d_off_y = 0;
159 1211 : if (ctx->src_x>=0) {
160 1211 : s_off_x = ctx->src_x;
161 : } else {
162 0 : d_off_x = (u32) (-ctx->src_x);
163 0 : copy_w = (s32) ctx->dst_width + ctx->src_x;
164 : do_memset = GF_TRUE;
165 : }
166 1211 : if (s_off_x + copy_w > ctx->w) {
167 0 : copy_w = ctx->w - s_off_x;
168 : do_memset = GF_TRUE;
169 : }
170 1211 : if (ctx->src_y>=0) {
171 1211 : s_off_y = ctx->src_y;
172 : } else {
173 0 : d_off_y = (u32) (-ctx->src_y);
174 0 : copy_h = (s32) ctx->dst_height + ctx->src_y;
175 : do_memset = GF_TRUE;
176 : }
177 1211 : if (s_off_y + copy_h > ctx->h) {
178 0 : copy_h = ctx->h - s_off_y;
179 : do_memset = GF_TRUE;
180 : }
181 :
182 1211 : if (ctx->use_reference) {
183 698 : GF_VCropFrame *vframe = gf_list_pop_back(ctx->frames_res);
184 698 : if (!vframe) {
185 21 : GF_SAFEALLOC(vframe, GF_VCropFrame);
186 21 : if (!vframe) return GF_OUT_OF_MEM;
187 : }
188 698 : vframe->ctx = ctx;
189 698 : memcpy(vframe->stride, ctx->src_stride, sizeof(vframe->stride));
190 698 : if (ctx->packed_422) {
191 108 : vframe->planes[0] = src_planes[0] + s_off_x * bps * 2 + ctx->src_stride[0] * s_off_y;
192 : } else {
193 590 : vframe->planes[0] = src_planes[0] + s_off_x * bps + ctx->src_stride[0] * s_off_y;
194 : }
195 : //nv12/21
196 698 : if (ctx->nb_planes==2) {
197 54 : vframe->planes[1] = src_planes[1] + s_off_x * bps + ctx->src_stride[1] * s_off_y/2;
198 644 : } else if (ctx->nb_planes>=3) {
199 : u32 div_x, div_y;
200 : //alpha/depth/other plane, treat as luma plane
201 347 : if (ctx->nb_planes==4) {
202 0 : vframe->planes[3] = src_planes[3] + s_off_x * bps + ctx->src_stride[3] * s_off_y;
203 : }
204 347 : div_x = (ctx->src_stride[1]==ctx->src_stride[0]) ? 1 : 2;
205 347 : div_y = (ctx->src_uv_height==ctx->h) ? 1 : 2;
206 :
207 347 : vframe->planes[1] = src_planes[1] + s_off_x * bps / div_x + ctx->src_stride[1] * s_off_y / div_y;
208 347 : vframe->planes[2] =src_planes[2] + s_off_x * bps / div_x + ctx->src_stride[2] * s_off_y / div_y;
209 : }
210 698 : vframe->frame_ifce.user_data = vframe;
211 698 : vframe->frame_ifce.get_plane = vcrop_frame_get_plane;
212 698 : dst_pck = gf_filter_pck_new_frame_interface(ctx->opid, &vframe->frame_ifce, vcrop_packet_destruct);
213 698 : if (!dst_pck) return GF_OUT_OF_MEM;
214 :
215 : //keep a ref to input packet
216 698 : vframe->pck = pck;
217 698 : gf_filter_pck_ref(&vframe->pck);
218 698 : gf_filter_pck_merge_properties(pck, dst_pck);
219 698 : gf_filter_pck_send(dst_pck);
220 : //remove input packet
221 698 : gf_filter_pid_drop_packet(ctx->ipid);
222 698 : return GF_OK;
223 : }
224 :
225 :
226 513 : dst_pck = gf_filter_pck_new_alloc(ctx->opid, ctx->out_size, &output);
227 513 : if (!dst_pck) return GF_OUT_OF_MEM;
228 :
229 513 : gf_filter_pck_merge_properties(pck, dst_pck);
230 513 : dst_planes[0] = output;
231 513 : if (ctx->nb_planes==1) {
232 216 : } else if (ctx->nb_planes==2) {
233 54 : dst_planes[1] = output + ctx->dst_stride[0] * ctx->dst_height;
234 162 : } else if (ctx->nb_planes==3) {
235 162 : dst_planes[1] = output + ctx->dst_stride[0] * ctx->dst_height;
236 162 : dst_planes[2] = dst_planes[1] + ctx->dst_stride[1]*ctx->dst_uv_height;
237 0 : } else if (ctx->nb_planes==4) {
238 0 : dst_planes[1] = output + ctx->dst_stride[0] * ctx->dst_height;
239 0 : dst_planes[2] = dst_planes[1] + ctx->dst_stride[1]*ctx->dst_uv_height;
240 0 : dst_planes[3] = dst_planes[2] + ctx->dst_stride[2]*ctx->dst_uv_height;
241 : } else {
242 0 : gf_filter_pid_drop_packet(ctx->ipid);
243 0 : return GF_NOT_SUPPORTED;
244 : }
245 :
246 513 : if (do_memset) {
247 0 : memset(output, 0x00, sizeof(char)*ctx->out_size);
248 : }
249 :
250 : //YUYV variations need *2 on horizontal dimension
251 513 : if (ctx->packed_422) {
252 108 : src = src_planes[0] + s_off_x * bps * 2 + ctx->src_stride[0] * s_off_y;
253 108 : dst = dst_planes[0] + d_off_x * bps * 2 + ctx->dst_stride[0] * d_off_y;
254 7020 : for (i=0; i<copy_h; i++) {
255 6912 : memcpy(dst, src, bps * copy_w * 2);
256 6912 : src += ctx->src_stride[0];
257 6912 : dst += ctx->dst_stride[0];
258 : }
259 : } else {
260 : //copy first plane
261 405 : src = src_planes[0] + s_off_x * bps + ctx->src_stride[0] * s_off_y;
262 405 : dst = dst_planes[0] + d_off_x * bps + ctx->dst_stride[0] * d_off_y;
263 26325 : for (i=0; i<copy_h; i++) {
264 25920 : memcpy(dst, src, bps * copy_w);
265 25920 : src += ctx->src_stride[0];
266 25920 : dst += ctx->dst_stride[0];
267 : }
268 : }
269 :
270 : //nv12/21
271 513 : if (ctx->nb_planes==2) {
272 54 : src = src_planes[1] + s_off_x * bps + ctx->src_stride[1] * s_off_y/2;
273 54 : dst = dst_planes[1] + d_off_x * bps + ctx->dst_stride[1] * d_off_y/2;
274 : //half vertical res (/2)
275 1782 : for (i=0; i<copy_h/2; i++) {
276 : //half horizontal res (/2) but two chroma packed per pixel (*2)
277 1728 : memcpy(dst, src, bps * copy_w);
278 1728 : src += ctx->src_stride[1];
279 1728 : dst += ctx->dst_stride[1];
280 : }
281 459 : } 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 162 : if (ctx->nb_planes==4) {
285 0 : src = src_planes[3] + s_off_x * bps + ctx->src_stride[3] * s_off_y;
286 0 : dst = dst_planes[3] + d_off_x * bps + ctx->dst_stride[3] * d_off_y;
287 0 : for (i=0; i<copy_h; i++) {
288 0 : memcpy(dst, src, bps * copy_w);
289 0 : src += ctx->src_stride[3];
290 0 : dst += ctx->dst_stride[3];
291 : }
292 : }
293 :
294 162 : div_x = (ctx->src_stride[1]==ctx->src_stride[0]) ? 1 : 2;
295 162 : div_y = (ctx->src_uv_height==ctx->h) ? 1 : 2;
296 :
297 162 : src = src_planes[1] + s_off_x * bps / div_x + ctx->src_stride[1] * s_off_y / div_y;
298 162 : dst = dst_planes[1] + d_off_x * bps / div_x + ctx->dst_stride[1] * d_off_y / div_y;
299 162 : copy_h /= div_y;
300 162 : copy_w /= div_x;
301 :
302 8802 : for (i=0; i<copy_h; i++) {
303 8640 : memcpy(dst, src, bps * copy_w);
304 8640 : src += ctx->src_stride[1];
305 8640 : dst += ctx->dst_stride[1];
306 : }
307 :
308 162 : src = src_planes[2] + s_off_x * bps / div_x + ctx->src_stride[2] * s_off_y / div_y;
309 162 : dst = dst_planes[2] + d_off_x * bps / div_x + ctx->dst_stride[2] * d_off_y / div_y;
310 8802 : for (i=0; i<copy_h; i++) {
311 8640 : memcpy(dst, src, bps * copy_w);
312 8640 : src += ctx->src_stride[1];
313 8640 : dst += ctx->dst_stride[1];
314 : }
315 : }
316 :
317 513 : gf_filter_pck_send(dst_pck);
318 513 : gf_filter_pid_drop_packet(ctx->ipid);
319 513 : return GF_OK;
320 : }
321 :
322 42 : 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 42 : 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 168 : while (args) {
335 : char *sep_offset=NULL, *sep_pc, sign=0;
336 168 : char *sep = strchr(args, 'x');
337 168 : if (sep) sep[0] = 0;
338 :
339 168 : sep_pc = strchr(args, '%');
340 168 : if (sep_pc) {
341 0 : sep_pc[0] = 0;
342 0 : dim[nb_dim] = atoi(args);
343 0 : sep_pc[0] = '%';
344 0 : dim_pc[nb_dim] = GF_TRUE;
345 :
346 0 : sep_offset = strchr(sep_pc, '+');
347 0 : if (sep_offset) {
348 0 : sign = sep_offset[0];
349 0 : dim_off[nb_dim] = atoi(sep_offset+1);
350 0 : sep_offset[0] = 0;
351 : } else {
352 0 : sep_offset = strchr(args, '-');
353 0 : if (sep_offset) {
354 0 : sign = sep_offset[0];
355 0 : dim_off[nb_dim] = - atoi(sep_offset+1);
356 0 : sep_offset[0] = 0;
357 : }
358 : }
359 :
360 : } else {
361 168 : dim[nb_dim] = atoi(args);
362 : }
363 :
364 0 : if (sep_offset) sep_offset[0] = sign;
365 168 : nb_dim++;
366 168 : if (!sep) break;
367 126 : sep[0] = 'x';
368 126 : args = sep+1;
369 126 : 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 42 : if (nb_dim==2) {
379 0 : ctx->src_x = 0;
380 0 : ctx->src_y = 0;
381 0 : GET_DIM(ctx->dst_width, 0, src_w)
382 0 : GET_DIM(ctx->dst_height, 1, src_h)
383 42 : } else if (nb_dim==4) {
384 42 : GET_DIM(ctx->src_x, 0, src_w)
385 42 : GET_DIM(ctx->src_y, 1, src_h)
386 42 : GET_DIM(ctx->dst_width, 2, src_w)
387 42 : GET_DIM(ctx->dst_height, 3, src_h)
388 : } else {
389 0 : 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 42 : if ((s32) ctx->dst_width < 0) {
393 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[VCrop] Negative destination width %d !\n", (s32) ctx->dst_width));
394 : return GF_FALSE;
395 : }
396 42 : if ((s32) ctx->dst_height < 0) {
397 0 : 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 42 : ctx->packed_422 = GF_FALSE;
405 42 : switch (pfmt) {
406 12 : case GF_PIXEL_YUV:
407 : case GF_PIXEL_YVU:
408 : case GF_PIXEL_NV21:
409 : case GF_PIXEL_NV21_10:
410 : case GF_PIXEL_NV12:
411 : case GF_PIXEL_NV12_10:
412 : case GF_PIXEL_YUVA:
413 : case GF_PIXEL_YUVD:
414 : case GF_PIXEL_YUV_10:
415 12 : if (ctx->src_x % 2) ROUND_IT(ctx->src_x);
416 12 : if (ctx->src_y % 2) ROUND_IT(ctx->src_y);
417 12 : if ((ctx->src_x + ctx->dst_width) % 2) ROUND_IT(ctx->dst_width);
418 12 : if ((ctx->src_y + ctx->dst_height) % 2) ROUND_IT(ctx->dst_height);
419 : break;
420 : //for YUV 422, adjust to multiple of 2 on horizontal dim
421 8 : case GF_PIXEL_YUYV:
422 : case GF_PIXEL_YVYU:
423 : case GF_PIXEL_UYVY:
424 : case GF_PIXEL_VYUY:
425 8 : ctx->packed_422 = GF_TRUE;
426 12 : case GF_PIXEL_YUV422:
427 : case GF_PIXEL_YUV422_10:
428 12 : if (ctx->src_x % 2) ROUND_IT(ctx->src_x);
429 12 : if ((ctx->src_x + ctx->dst_width) % 2) ROUND_IT(ctx->dst_width);
430 12 : if (ctx->round>1) {
431 0 : if (ctx->src_y % 2) ROUND_IT(ctx->src_y);
432 0 : if ((ctx->src_y + ctx->dst_height) % 2) ROUND_IT(ctx->dst_height);
433 : }
434 : break;
435 18 : default:
436 18 : if (ctx->round>1) {
437 0 : if (ctx->src_x % 2) ROUND_IT(ctx->src_x);
438 0 : if (ctx->src_y % 2) ROUND_IT(ctx->src_y);
439 0 : if ((ctx->src_x + ctx->dst_width) % 2) ROUND_IT(ctx->dst_width);
440 0 : if ((ctx->src_y + ctx->dst_height) % 2) ROUND_IT(ctx->dst_height);
441 : }
442 : break;
443 : }
444 : return GF_TRUE;
445 : }
446 :
447 42 : static GF_Err vcrop_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
448 : {
449 : GF_PropVec2i vec;
450 : const GF_PropertyValue *p;
451 : u32 w, h, stride, pfmt;
452 : GF_Fraction sar;
453 42 : GF_VCropCtx *ctx = gf_filter_get_udta(filter);
454 :
455 42 : if (is_remove) {
456 0 : if (ctx->opid) {
457 0 : gf_filter_pid_remove(ctx->opid);
458 0 : ctx->opid = NULL;
459 : }
460 : return GF_OK;
461 : }
462 42 : if (! gf_filter_pid_check_caps(pid))
463 : return GF_NOT_SUPPORTED;
464 :
465 42 : if (!ctx->opid) {
466 40 : ctx->opid = gf_filter_pid_new(filter);
467 : }
468 : //copy properties at init or reconfig
469 42 : gf_filter_pid_copy_properties(ctx->opid, pid);
470 :
471 42 : if (!ctx->ipid) {
472 40 : ctx->ipid = pid;
473 : }
474 : w = h = pfmt = stride = 0;
475 42 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_WIDTH);
476 42 : if (p) w = p->value.uint;
477 42 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_HEIGHT);
478 42 : if (p) h = p->value.uint;
479 42 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_STRIDE);
480 42 : if (p) stride = p->value.uint;
481 42 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_PIXFMT);
482 42 : if (p) pfmt = p->value.uint;
483 42 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_SAR);
484 42 : if (p) sar = p->value.frac;
485 : else sar.den = sar.num = 1;
486 :
487 42 : if (!w || !h || !pfmt) {
488 0 : ctx->passthrough = GF_TRUE;
489 0 : return GF_OK;
490 : }
491 42 : ctx->passthrough = GF_FALSE;
492 :
493 42 : if ((ctx->w == w) && (ctx->h == h) && (ctx->s_pfmt == pfmt) && (ctx->stride == stride)) {
494 : //nothing to reconfigure
495 : } else {
496 : Bool res;
497 42 : if (! parse_crop_props(ctx, w, h, pfmt)) {
498 : return GF_BAD_PARAM;
499 : }
500 :
501 42 : if (ctx->src_x<0) ctx->use_reference = GF_FALSE;
502 42 : else if (ctx->src_y<0) ctx->use_reference = GF_FALSE;
503 42 : else if (ctx->src_x + ctx->dst_width > w) ctx->use_reference = GF_FALSE;
504 42 : else if (ctx->src_y + ctx->dst_height > h) ctx->use_reference = GF_FALSE;
505 42 : else ctx->use_reference = ctx->copy ? GF_FALSE : GF_TRUE;
506 :
507 42 : if (ctx->use_reference) {
508 23 : if (!ctx->frames) ctx->frames = gf_list_new();
509 23 : if (!ctx->frames_res) ctx->frames_res = gf_list_new();
510 : }
511 :
512 : //get layout info for source
513 42 : memset(ctx->src_stride, 0, sizeof(ctx->src_stride));
514 42 : if (ctx->stride) ctx->src_stride[0] = ctx->stride;
515 :
516 42 : 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);
517 42 : if (!res) {
518 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[VCrop] Failed to query source pixel format characteristics\n"));
519 : return GF_NOT_SUPPORTED;
520 : }
521 42 : if (ctx->nb_src_planes==3) ctx->src_stride[2] = ctx->src_stride[1];
522 42 : if (ctx->nb_src_planes==4) ctx->src_stride[3] = ctx->src_stride[0];
523 :
524 :
525 : //get layout info for dest
526 42 : memset(ctx->dst_stride, 0, sizeof(ctx->dst_stride));
527 42 : 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);
528 42 : if (!res) {
529 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[VCrop] Failed to query output pixel format characteristics\n"));
530 : return GF_NOT_SUPPORTED;
531 : }
532 42 : if (ctx->nb_planes==3) ctx->dst_stride[2] = ctx->dst_stride[1];
533 42 : if (ctx->nb_planes==4) ctx->dst_stride[3] = ctx->dst_stride[0];
534 :
535 :
536 42 : ctx->w = w;
537 42 : ctx->h = h;
538 42 : ctx->s_pfmt = pfmt;
539 :
540 42 : 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));
541 :
542 42 : if (!ctx->src_x && !ctx->src_y && (ctx->dst_width==ctx->w) && (ctx->dst_height==ctx->h) ) {
543 0 : ctx->passthrough = GF_TRUE;
544 : }
545 : }
546 :
547 42 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_WIDTH, &PROP_UINT(ctx->dst_width));
548 42 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_HEIGHT, &PROP_UINT(ctx->dst_height));
549 42 : if (ctx->use_reference) {
550 23 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STRIDE, &PROP_UINT(ctx->src_stride[0] ));
551 23 : if (ctx->nb_planes>1)
552 12 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STRIDE_UV, &PROP_UINT(ctx->src_stride[1]));
553 : } else {
554 19 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STRIDE, &PROP_UINT(ctx->dst_stride[0] ));
555 19 : if (ctx->nb_planes>1)
556 8 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STRIDE_UV, &PROP_UINT(ctx->dst_stride[1]));
557 : }
558 42 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SAR, &PROP_FRAC(sar) );
559 42 : vec.x = ctx->src_x;
560 42 : vec.y = ctx->src_y;
561 42 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CROP_POS, &PROP_VEC2I(vec) );
562 42 : vec.x = ctx->w;
563 42 : vec.y = ctx->h;
564 42 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_ORIG_SIZE, &PROP_VEC2I(vec) );
565 :
566 42 : return GF_OK;
567 : }
568 :
569 40 : void vcrop_finalize(GF_Filter *filter)
570 : {
571 40 : GF_VCropCtx *ctx = gf_filter_get_udta(filter);
572 40 : if (ctx->frames) {
573 21 : while (gf_list_count(ctx->frames)) {
574 0 : GF_VCropFrame *vframe = gf_list_pop_back(ctx->frames);
575 0 : gf_free(vframe);
576 : }
577 21 : gf_list_del(ctx->frames);
578 : }
579 40 : if (ctx->frames_res) {
580 42 : while (gf_list_count(ctx->frames_res)) {
581 21 : GF_VCropFrame *vframe = gf_list_pop_back(ctx->frames_res);
582 21 : gf_free(vframe);
583 : }
584 21 : gf_list_del(ctx->frames_res);
585 : }
586 40 : }
587 :
588 :
589 : #define OFFS(_n) #_n, offsetof(GF_VCropCtx, _n)
590 : static GF_FilterArgs VCropArgs[] =
591 : {
592 : { 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},
593 : { 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},
594 : { OFFS(round), "adjust dimension to be a multiple of 2\n"
595 : "- up: up rounding\n"
596 : "- down: down rounding\n"
597 : "- allup: up rounding on formats that do not require it (RGB, YUV444)\n"
598 : "- 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},
599 : {0}
600 : };
601 :
602 : static const GF_FilterCapability VCropCaps[] =
603 : {
604 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
605 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_RAW),
606 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
607 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_RAW),
608 : };
609 :
610 : GF_FilterRegister VCropRegister = {
611 : .name = "vcrop",
612 : GF_FS_SET_DESCRIPTION("Video crop")
613 : GF_FS_SET_HELP("This filter is used to crop raw video data.")
614 : .private_size = sizeof(GF_VCropCtx),
615 : .flags = GF_FS_REG_EXPLICIT_ONLY,
616 : .args = VCropArgs,
617 : .configure_pid = vcrop_configure_pid,
618 : SETCAPS(VCropCaps),
619 : .process = vcrop_process,
620 : .finalize = vcrop_finalize,
621 : };
622 :
623 :
624 :
625 2877 : const GF_FilterRegister *vcrop_register(GF_FilterSession *session)
626 : {
627 2877 : return &VCropRegister;
628 : }
|