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