Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Samir Mustapha - Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2019-2021
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / video flip 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 : u32 mode;
35 :
36 : //internal data
37 : Bool initialized;
38 :
39 : GF_FilterPid *ipid, *opid;
40 : u32 w, h, stride, s_pfmt;
41 : u32 bps;
42 : GF_Fraction ar;
43 : Bool passthrough;
44 : u32 dst_width, dst_height;
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 : Bool packed_422;
52 :
53 :
54 : char *line_buffer_vf; //vertical flip
55 : char *line_buffer_hf; //horizontal flip
56 :
57 : } GF_VFlipCtx;
58 :
59 : enum
60 : {
61 : VFLIP_OFF = 0,
62 : VFLIP_VERT,
63 : VFLIP_HORIZ,
64 : VFLIP_BOTH,
65 : };
66 :
67 :
68 :
69 884736 : static void swap_2Ys_YUVpixel(GF_VFlipCtx *ctx, u8 *line_src, u8 *line_dst, u32 FourBytes_start_index)
70 : {
71 : u32 isFirstY_indexOne;
72 884736 : switch (ctx->s_pfmt) {
73 : case GF_PIXEL_YUYV:
74 : case GF_PIXEL_YVYU:
75 : isFirstY_indexOne= (u32) 0;
76 : break;
77 442368 : case GF_PIXEL_UYVY:
78 : case GF_PIXEL_VYUY:
79 : isFirstY_indexOne= (u32) 1;
80 : break;
81 : default:
82 : return;
83 : }
84 :
85 : //Y2_dst = Y1_src
86 884736 : line_dst[FourBytes_start_index + 2 + isFirstY_indexOne]=line_src[FourBytes_start_index + 0 + isFirstY_indexOne];
87 :
88 : //Y1_dst = Y2_src
89 884736 : line_dst[FourBytes_start_index + 0 + isFirstY_indexOne]=line_src[FourBytes_start_index + 2 + isFirstY_indexOne];
90 : }
91 :
92 110592 : static void horizontal_flip_per_line(GF_VFlipCtx *ctx, u8 *line_src, u8 *line_dst, u32 plane_idx, u32 wiB)
93 : {
94 : u32 j, line_size = wiB;
95 :
96 110592 : if (ctx->s_pfmt == GF_PIXEL_RGB || ctx->s_pfmt == GF_PIXEL_BGR || ctx->s_pfmt == GF_PIXEL_XRGB || ctx->s_pfmt == GF_PIXEL_RGBX || ctx->s_pfmt == GF_PIXEL_XBGR || ctx->s_pfmt == GF_PIXEL_BGRX){
97 : //to avoid "3*j > line_size - 3*j - 3" or "4*j > line_size - 4*j - 4"
98 : //jmax=line_size/(2*3) or jmax=line_size/(2*4)
99 1347840 : for (j=0; j < line_size/(2*ctx->bps); j++) {
100 : u8 pix[4];
101 1327104 : memcpy(pix, line_src + line_size - ctx->bps*(j+1), ctx->bps);
102 1327104 : memcpy(line_dst + line_size - ctx->bps*(j+1), line_src + ctx->bps*j, ctx->bps);
103 1327104 : memcpy(line_dst + ctx->bps*j, pix, ctx->bps);
104 : }
105 89856 : } else if (ctx->packed_422) {
106 : //If the source data is assigned to the output packet during the destination pack allocation
107 : //i.e dst_planes[0]= src_planes[0], line_src is going to change while reading it as far as writing on line_dst=line_src
108 : //To avoid this situation, ctx->line_buffer_hf keeps the values of line_src
109 13824 : memcpy(ctx->line_buffer_hf, line_src, wiB);
110 :
111 : //reversing of 4-bytes sequences
112 13824 : u32 fourBytesSize = wiB/4;
113 898560 : for (j=0; j < fourBytesSize; j++) {
114 : //buffer = last 4 columns
115 884736 : u32 last_4bytes_index = wiB-4-(4*j);
116 : u32 p, first_4bytes_index = 4*j;
117 4423680 : for (p = 0; p < 4; p++) {
118 3538944 : line_dst[first_4bytes_index+p] = ctx->line_buffer_hf[last_4bytes_index+p];
119 : }
120 : //exchanging of Ys within a yuv pixel
121 884736 : swap_2Ys_YUVpixel(ctx, line_dst, line_dst, first_4bytes_index);
122 : }
123 : } else {
124 : //nv12/21
125 : //second plane is U-plane={u1,v1, u2,v2...}
126 76032 : if (ctx->nb_planes==2 && plane_idx==1){
127 3456 : if (ctx->bps==1) {
128 : //to avoid "line_size - 2*j - 2 > 2*j", jmax=line_size/4
129 110592 : for (j=0; j < line_size/4; j++) {
130 : u8 u_comp, v_comp;
131 110592 : u_comp = line_src[line_size - 2*j - 2];
132 110592 : v_comp = line_src[line_size - 2*j - 1];
133 :
134 110592 : line_dst[line_size - 2*j - 2] = line_src[2*j];
135 110592 : line_dst[line_size - 2*j - 1] = line_src[2*j + 1];
136 :
137 110592 : line_dst[2*j] = u_comp;
138 110592 : line_dst[2*j + 1] = v_comp;
139 : }
140 : } else {
141 0 : for (j=0; j < line_size/4; j++) {
142 : u16 u_comp, v_comp;
143 0 : u_comp = line_src[line_size - 2*j - 2];
144 0 : v_comp = line_src[line_size - 2*j - 1];
145 :
146 0 : ((u16 *)line_dst)[line_size - 2*j - 2] = ((u16 *)line_src)[2*j];
147 0 : ((u16 *)line_dst)[line_size - 2*j - 1] = ((u16 *)line_src)[2*j + 1];
148 :
149 0 : ((u16 *)line_dst)[2*j] = u_comp;
150 0 : ((u16 *)line_dst)[2*j + 1] = v_comp;
151 : }
152 : }
153 72576 : } else if (ctx->bps==1) {
154 44928 : u32 wx = line_size/2;
155 : u8 tmp;
156 2477952 : for (j=0; j < wx; j++) {
157 : //tmp = last column
158 2433024 : tmp = line_src[line_size-1-j];
159 :
160 : //last column = first column
161 2433024 : line_dst[line_size-1-j] = line_src[j];
162 :
163 : //first column = tmp
164 2433024 : line_dst[j]=tmp;
165 : }
166 : } else {
167 27648 : line_size /= 2;
168 27648 : u32 wx = line_size/2;
169 : u16 tmp;
170 1465344 : for (j=0; j < wx; j++) {
171 : //tmp = last column
172 1437696 : tmp = (u16) ( ((u16 *)line_src) [line_size-1-j] );
173 :
174 : //last column = first column
175 1437696 : ((u16 *)line_dst) [line_size-1-j] = ((u16*)line_src)[j];
176 :
177 : //first column = tmp
178 1437696 : ((u16 *)line_dst) [j]=tmp;
179 : }
180 : }
181 : }
182 110592 : }
183 :
184 972 : static void horizontal_flip(GF_VFlipCtx *ctx, u8 *src_plane, u8 *dst_plane, u32 height, u32 plane_idx, u32 wiB, u32 *src_stride)
185 : {
186 : u32 i;
187 111564 : for (i=0; i<height; i++) {
188 110592 : u8 *src_first_line = src_plane + i * src_stride[plane_idx];
189 110592 : u8 *dst_first_line = dst_plane + i * ctx->dst_stride[plane_idx];
190 :
191 110592 : horizontal_flip_per_line(ctx, src_first_line, dst_first_line, plane_idx, wiB);
192 : }
193 972 : }
194 :
195 972 : static void vertical_flip(GF_VFlipCtx *ctx, u8 *src_plane, u8 *dst_plane, u32 height, u32 plane_idx, u32 wiB){
196 : u32 hy, i;
197 972 : hy = height/2;
198 56268 : for (i=0; i<hy; i++) {
199 55296 : u8 *src_first_line = src_plane+ i*ctx->src_stride[plane_idx];
200 55296 : u8 *src_last_line = src_plane+ (height - 1 - i) * ctx->src_stride[plane_idx];
201 :
202 55296 : u8 *dst_first_line = dst_plane+ i*ctx->dst_stride[plane_idx];
203 55296 : u8 *dst_last_line = dst_plane+ (height - 1 - i) * ctx->dst_stride[plane_idx];
204 :
205 55296 : memcpy(ctx->line_buffer_vf, src_last_line, wiB);
206 : memcpy(dst_last_line, src_first_line, wiB);
207 55296 : memcpy(dst_first_line, ctx->line_buffer_vf, wiB);
208 : }
209 972 : }
210 :
211 2160 : static GF_Err vflip_process(GF_Filter *filter)
212 : {
213 : const char *data;
214 : u8 *output;
215 : u32 size;
216 : u32 i;
217 : u32 wiB, height; //wiB: width in Bytes of a plane
218 : u8 *src_planes[5];
219 : u8 *dst_planes[5];
220 : GF_FilterPacket *dst_pck;
221 : GF_FilterFrameInterface *frame_ifce;
222 2160 : GF_VFlipCtx *ctx = gf_filter_get_udta(filter);
223 2160 : GF_FilterPacket *pck = gf_filter_pid_get_packet(ctx->ipid);
224 :
225 2160 : if (!pck) {
226 1080 : if (gf_filter_pid_is_eos(ctx->ipid)) {
227 40 : gf_filter_pid_set_eos(ctx->opid);
228 40 : return GF_EOS;
229 : }
230 : return GF_OK;
231 : }
232 :
233 1080 : if (ctx->passthrough) {
234 27 : gf_filter_pck_forward(pck, ctx->opid);
235 27 : gf_filter_pid_drop_packet(ctx->ipid);
236 27 : return GF_OK;
237 : }
238 1053 : data = gf_filter_pck_get_data(pck, &size);
239 1053 : frame_ifce = gf_filter_pck_get_frame_interface(pck);
240 :
241 : memset(src_planes, 0, sizeof(src_planes));
242 : memset(dst_planes, 0, sizeof(dst_planes));
243 :
244 1053 : if (data) {
245 1053 : src_planes[0] = (u8 *) data;
246 :
247 1053 : if (ctx->nb_src_planes==1) {
248 459 : } else if (ctx->nb_src_planes==2) {
249 108 : src_planes[1] = src_planes[0] + ctx->src_stride[0]*ctx->h;
250 351 : } else if (ctx->nb_src_planes==3) {
251 351 : src_planes[1] = src_planes[0] + ctx->src_stride[0] * ctx->h;
252 351 : src_planes[2] = src_planes[1] + ctx->src_stride[1] * ctx->src_uv_height;
253 0 : } else if (ctx->nb_src_planes==4) {
254 0 : src_planes[1] = src_planes[0] + ctx->src_stride[0] * ctx->h;
255 0 : src_planes[2] = src_planes[1] + ctx->src_stride[1] * ctx->src_uv_height;
256 0 : src_planes[3] = src_planes[2] + ctx->src_stride[2] * ctx->src_uv_height;
257 : }
258 0 : } else if (frame_ifce && frame_ifce->get_plane) {
259 0 : for (i=0; i<4; i++) {
260 0 : if (frame_ifce->get_plane(frame_ifce, i, (const u8 **) &src_planes[i], &ctx->src_stride[i])!=GF_OK)
261 : break;
262 : }
263 : } else {
264 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[VFlip] No data associated with packet, not supported\n"));
265 0 : gf_filter_pid_drop_packet(ctx->ipid);
266 0 : return GF_NOT_SUPPORTED;
267 : }
268 :
269 1053 : ctx->bps = gf_pixel_get_bytes_per_pixel(ctx->s_pfmt);
270 :
271 :
272 1053 : if (frame_ifce){
273 0 : dst_pck = gf_filter_pck_new_alloc(ctx->opid, ctx->out_size, &output);
274 0 : if (dst_pck)
275 0 : gf_filter_pck_merge_properties(pck, dst_pck);
276 : } else {
277 1053 : dst_pck = gf_filter_pck_new_clone(ctx->opid, pck, &output);
278 : }
279 1053 : if (!dst_pck) return GF_OUT_OF_MEM;
280 :
281 1053 : dst_planes[0] = output;
282 1053 : if (ctx->nb_planes==1) {
283 459 : } else if (ctx->nb_planes==2) {
284 108 : dst_planes[1] = output + ctx->dst_stride[0] * ctx->dst_height;
285 351 : } else if (ctx->nb_planes==3) {
286 351 : dst_planes[1] = output + ctx->dst_stride[0] * ctx->dst_height;
287 351 : dst_planes[2] = dst_planes[1] + ctx->dst_stride[1]*ctx->dst_uv_height;
288 0 : } else if (ctx->nb_planes==4) {
289 0 : dst_planes[1] = output + ctx->dst_stride[0] * ctx->dst_height;
290 0 : dst_planes[2] = dst_planes[1] + ctx->dst_stride[1]*ctx->dst_uv_height;
291 0 : dst_planes[3] = dst_planes[2] + ctx->dst_stride[2]*ctx->dst_uv_height;
292 : }
293 :
294 : //computing of height and wiB
295 : //YUYV variations need *2 on horizontal dimension
296 1863 : for (i=0; i<ctx->nb_planes; i++) {
297 1863 : if (i==0) {
298 1053 : if (ctx->packed_422) {
299 216 : wiB = ctx->bps * ctx->dst_width *2;
300 : } else {
301 837 : wiB = ctx->bps * ctx->dst_width;
302 : }
303 1053 : height = ctx->h;
304 : }else {
305 : //nv12/21
306 810 : if (i==1 && ctx->nb_planes==2) {
307 : //half vertical res (/2)
308 : //half horizontal res (/2) but two chroma packed per pixel (*2)
309 108 : wiB = ctx->bps * ctx->dst_width;
310 108 : height = ctx->h / 2;
311 702 : } else if (ctx->nb_planes>=3) {
312 : u32 div_x, div_y;
313 : //alpha/depth/other plane, treat as luma plane
314 702 : if (i==3 && ctx->nb_planes==4) {
315 0 : wiB = ctx->bps * ctx->dst_width;
316 0 : height = ctx->h;
317 702 : }else if (i==1 || i==2) {
318 702 : div_x = (ctx->src_stride[1]==ctx->src_stride[0]) ? 1 : 2;
319 702 : div_y = (ctx->src_uv_height==ctx->h) ? 1 : 2;
320 702 : height = ctx->dst_height;
321 702 : height /= div_y;
322 702 : wiB = ctx->bps * ctx->dst_width;
323 702 : wiB /= div_x;
324 : }
325 : }
326 : }
327 :
328 : //processing according selected mode
329 1863 : if (ctx->mode==VFLIP_VERT){
330 891 : vertical_flip(ctx, src_planes[i], dst_planes[i], height, i, wiB);
331 972 : }else if (ctx->mode==VFLIP_HORIZ){
332 891 : horizontal_flip(ctx, src_planes[i], dst_planes[i], height, i, wiB, ctx->src_stride);
333 81 : }else if (ctx->mode==VFLIP_BOTH){
334 81 : vertical_flip(ctx, src_planes[i], dst_planes[i], height, i, wiB);
335 81 : horizontal_flip(ctx, dst_planes[i], dst_planes[i], height, i, wiB, ctx->dst_stride);
336 : }
337 : }
338 :
339 1053 : gf_filter_pck_send(dst_pck);
340 1053 : gf_filter_pid_drop_packet(ctx->ipid);
341 1053 : return GF_OK;
342 : }
343 :
344 :
345 40 : static GF_Err vflip_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
346 : {
347 : const GF_PropertyValue *p;
348 : u32 w, h, stride, pfmt;
349 : GF_Fraction sar;
350 40 : GF_VFlipCtx *ctx = gf_filter_get_udta(filter);
351 :
352 40 : if (is_remove) {
353 0 : if (ctx->opid) {
354 0 : gf_filter_pid_remove(ctx->opid);
355 0 : ctx->opid = NULL;
356 : }
357 : return GF_OK;
358 : }
359 40 : if (! gf_filter_pid_check_caps(pid))
360 : return GF_NOT_SUPPORTED;
361 :
362 40 : if (!ctx->opid) {
363 40 : ctx->opid = gf_filter_pid_new(filter);
364 : }
365 : //copy properties at init or reconfig
366 40 : gf_filter_pid_copy_properties(ctx->opid, pid);
367 :
368 40 : if (!ctx->ipid) {
369 40 : ctx->ipid = pid;
370 : }
371 : w = h = pfmt = stride = 0;
372 40 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_WIDTH);
373 40 : if (p) w = p->value.uint;
374 40 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_HEIGHT);
375 40 : if (p) h = p->value.uint;
376 40 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_STRIDE);
377 40 : if (p) stride = p->value.uint;
378 40 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_PIXFMT);
379 40 : if (p) pfmt = p->value.uint;
380 40 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_SAR);
381 40 : if (p) sar = p->value.frac;
382 : else sar.den = sar.num = 1;
383 :
384 40 : if (!w || !h || !pfmt) {
385 0 : ctx->passthrough = GF_TRUE;
386 0 : return GF_OK;
387 : }
388 :
389 40 : if ((ctx->w == w) && (ctx->h == h) && (ctx->s_pfmt == pfmt) && (ctx->stride == stride)) {
390 : //nothing to reconfigure
391 0 : ctx->passthrough = GF_TRUE;
392 40 : } else if (ctx->mode==VFLIP_OFF) {
393 1 : ctx->passthrough = GF_TRUE;
394 : } else {
395 : Bool res;
396 :
397 39 : ctx->w = w;
398 39 : ctx->h = h;
399 39 : ctx->s_pfmt = pfmt;
400 39 : ctx->stride = stride;
401 39 : ctx->dst_width = w;
402 39 : ctx->dst_height = h;
403 39 : ctx->passthrough = GF_FALSE;
404 :
405 : //get layout info for source
406 39 : memset(ctx->src_stride, 0, sizeof(ctx->src_stride));
407 39 : if (ctx->stride) ctx->src_stride[0] = ctx->stride;
408 :
409 39 : 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);
410 39 : if (!res) {
411 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[VFlip] Failed to query source pixel format characteristics\n"));
412 : return GF_NOT_SUPPORTED;
413 : }
414 39 : if (ctx->nb_src_planes==3) ctx->src_stride[2] = ctx->src_stride[1];
415 39 : if (ctx->nb_src_planes==4) ctx->src_stride[3] = ctx->src_stride[0];
416 :
417 :
418 : //get layout info for dest
419 39 : memset(ctx->dst_stride, 0, sizeof(ctx->dst_stride));
420 39 : 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);
421 39 : if (!res) {
422 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[VFlip] Failed to query output pixel format characteristics\n"));
423 : return GF_NOT_SUPPORTED;
424 : }
425 39 : if (ctx->nb_planes==3) ctx->dst_stride[2] = ctx->dst_stride[1];
426 39 : if (ctx->nb_planes==4) ctx->dst_stride[3] = ctx->dst_stride[0];
427 :
428 :
429 39 : ctx->w = w;
430 39 : ctx->h = h;
431 39 : ctx->s_pfmt = pfmt;
432 :
433 39 : GF_LOG(GF_LOG_INFO, GF_LOG_MEDIA, ("[VFlip] Configured output full frame size %dx%d\n", ctx->w, ctx->h));
434 :
435 39 : ctx->line_buffer_vf = gf_realloc(ctx->line_buffer_vf, sizeof(char)*ctx->dst_stride[0] );
436 39 : ctx->line_buffer_hf = gf_realloc(ctx->line_buffer_hf, sizeof(char)*ctx->src_stride[0] );
437 :
438 39 : ctx->packed_422 = GF_FALSE;
439 39 : switch (pfmt) {
440 : //for YUV 422, adjust to multiple of 2 on horizontal dim
441 8 : case GF_PIXEL_YUYV:
442 : case GF_PIXEL_YVYU:
443 : case GF_PIXEL_UYVY:
444 : case GF_PIXEL_VYUY:
445 8 : ctx->packed_422 = GF_TRUE;
446 8 : break;
447 : }
448 : }
449 :
450 40 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_WIDTH, &PROP_UINT(ctx->dst_width));
451 40 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_HEIGHT, &PROP_UINT(ctx->dst_height));
452 :
453 40 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STRIDE, &PROP_UINT(ctx->dst_stride[0] ));
454 40 : if (ctx->nb_planes>1)
455 17 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STRIDE_UV, &PROP_UINT(ctx->dst_stride[1]));
456 :
457 40 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SAR, &PROP_FRAC(sar) );
458 :
459 : //an access unit corresponds to a single packet
460 40 : gf_filter_pid_set_framing_mode(pid, GF_TRUE);
461 40 : return GF_OK;
462 : }
463 :
464 40 : void vflip_finalize(GF_Filter *filter)
465 : {
466 40 : GF_VFlipCtx *ctx = gf_filter_get_udta(filter);
467 40 : if (ctx->line_buffer_vf) gf_free(ctx->line_buffer_vf);
468 40 : if (ctx->line_buffer_hf) gf_free(ctx->line_buffer_hf);
469 40 : }
470 :
471 :
472 : #define OFFS(_n) #_n, offsetof(GF_VFlipCtx, _n)
473 : static GF_FilterArgs VFlipArgs[] =
474 : {
475 : { OFFS(mode), "flip mode\n"
476 : "- off: no flipping (passthrough)\n"
477 : "- vert: vertical flip\n"
478 : "- horiz: horizontal flip\n"
479 : "- both: horizontal and vertical flip"
480 : "", GF_PROP_UINT, "vert", "off|vert|horiz|both", GF_FS_ARG_UPDATE | GF_FS_ARG_HINT_ADVANCED},
481 : {0}
482 : };
483 :
484 : static const GF_FilterCapability VFlipCaps[] =
485 : {
486 : CAP_UINT(GF_CAPS_INPUT_OUTPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
487 : CAP_UINT(GF_CAPS_INPUT_OUTPUT,GF_PROP_PID_CODECID, GF_CODECID_RAW)
488 : };
489 :
490 : GF_FilterRegister VFlipRegister = {
491 : .name = "vflip",
492 : GF_FS_SET_DESCRIPTION("Video flip")
493 : GF_FS_SET_HELP("Filter used to flip video frames vertically, horizontally, in both directions or no flip")
494 : .private_size = sizeof(GF_VFlipCtx),
495 : .flags = GF_FS_REG_EXPLICIT_ONLY,
496 : .args = VFlipArgs,
497 : .configure_pid = vflip_configure_pid,
498 : SETCAPS(VFlipCaps),
499 : .process = vflip_process,
500 : .finalize = vflip_finalize,
501 : };
502 :
503 :
504 :
505 2877 : const GF_FilterRegister *vflip_register(GF_FilterSession *session)
506 : {
507 2877 : return &VFlipRegister;
508 : }
|