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 / ffmpeg video rescaler 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/setup.h>
27
28 #ifdef GPAC_HAS_FFMPEG
29
30 #include "ff_common.h"
31
32 typedef struct
33 {
34 //options
35 GF_PropVec2i osize;
36 u32 ofmt, scale;
37 Double p1, p2;
38
39 //internal data
40 Bool initialized;
41
42 GF_FilterPid *ipid, *opid;
43 u32 w, h, stride, s_pfmt;
44 GF_Fraction ar;
45 Bool passthrough;
46
47 struct SwsContext *swscaler;
48
49 u32 dst_stride[5];
50 u32 src_stride[5];
51 u32 nb_planes, nb_src_planes, out_size, out_src_size, src_uv_height, dst_uv_height, ow, oh;
52
53 u32 swap_idx_1, swap_idx_2;
54
55 } GF_FFSWScaleCtx;
56
ffsws_process(GF_Filter * filter)57 static GF_Err ffsws_process(GF_Filter *filter)
58 {
59 const char *data;
60 u8 *output;
61 u32 osize;
62 s32 res;
63 u8 *src_planes[5];
64 u8 *dst_planes[5];
65 GF_FilterPacket *dst_pck;
66 GF_FilterFrameInterface *frame_ifce;
67 GF_FFSWScaleCtx *ctx = gf_filter_get_udta(filter);
68 GF_FilterPacket *pck;
69
70 pck = gf_filter_pid_get_packet(ctx->ipid);
71
72 if (!pck) {
73 if (gf_filter_pid_is_eos(ctx->ipid)) {
74 gf_filter_pid_set_eos(ctx->opid);
75 return GF_EOS;
76 }
77 return GF_OK;
78 }
79
80 if (ctx->passthrough) {
81 gf_filter_pck_forward(pck, ctx->opid);
82 gf_filter_pid_drop_packet(ctx->ipid);
83 return GF_OK;
84 }
85 //not yet configured
86 if (!ctx->ofmt && !ctx->ow && !ctx->oh)
87 return GF_OK;
88
89 if (!ctx->swscaler) {
90 gf_filter_pid_drop_packet(ctx->ipid);
91 return GF_NOT_SUPPORTED;
92 }
93
94 data = gf_filter_pck_get_data(pck, &osize);
95 frame_ifce = gf_filter_pck_get_frame_interface(pck);
96 //we may have biffer input (padding) but shall not have smaller
97 if (osize && (ctx->out_src_size > osize) ) {
98 GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[FFSWS] Mismatched in source osize, expected %d got %d - stride issue ?\n", ctx->out_src_size, osize));
99 gf_filter_pid_drop_packet(ctx->ipid);
100 return GF_NOT_SUPPORTED;
101 }
102
103 memset(src_planes, 0, sizeof(src_planes));
104 memset(dst_planes, 0, sizeof(dst_planes));
105 dst_pck = gf_filter_pck_new_alloc(ctx->opid, ctx->out_size, &output);
106 if (!dst_pck) {
107 gf_filter_pid_drop_packet(ctx->ipid);
108 return GF_OUT_OF_MEM;
109 }
110 gf_filter_pck_merge_properties(pck, dst_pck);
111
112 if (data) {
113 src_planes[0] = (u8 *) data;
114
115 if (ctx->nb_src_planes==1) {
116 } else if (ctx->nb_src_planes==2) {
117 src_planes[1] = src_planes[0] + ctx->src_stride[0]*ctx->h;
118 } else if (ctx->nb_src_planes==3) {
119 src_planes[1] = src_planes[0] + ctx->src_stride[0] * ctx->h;
120 src_planes[2] = src_planes[1] + ctx->src_stride[1] * ctx->src_uv_height;
121 } else if (ctx->nb_src_planes==4) {
122 src_planes[1] = src_planes[0] + ctx->src_stride[0] * ctx->h;
123 src_planes[2] = src_planes[1] + ctx->src_stride[1] * ctx->src_uv_height;
124 src_planes[3] = src_planes[2] + ctx->src_stride[2] * ctx->src_uv_height;
125 }
126 } else if (frame_ifce && frame_ifce->get_plane) {
127 u32 i=0;
128 for (i=0; i<ctx->nb_src_planes; i++) {
129 if (frame_ifce->get_plane(frame_ifce, i, (const u8 **) &src_planes[i], &ctx->src_stride[i])!=GF_OK)
130 break;
131 }
132 } else {
133 GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[FFSWS] No data associated with packet, not supported\n"));
134 gf_filter_pid_drop_packet(ctx->ipid);
135 return GF_NOT_SUPPORTED;
136 }
137 dst_planes[0] = output;
138 if (ctx->nb_planes==1) {
139 } else if (ctx->nb_planes==2) {
140 dst_planes[1] = output + ctx->dst_stride[0] * ctx->oh;
141 } else if (ctx->nb_planes==3) {
142 dst_planes[1] = output + ctx->dst_stride[0] * ctx->oh;
143 dst_planes[2] = dst_planes[1] + ctx->dst_stride[1]*ctx->dst_uv_height;
144 } else if (ctx->nb_planes==4) {
145 dst_planes[1] = output + ctx->dst_stride[0] * ctx->oh;
146 dst_planes[2] = dst_planes[1] + ctx->dst_stride[1]*ctx->dst_uv_height;
147 dst_planes[3] = dst_planes[2] + ctx->dst_stride[2]*ctx->dst_uv_height;
148 }
149
150 //rescale the cropped frame
151 res = sws_scale(ctx->swscaler, (const u8**) src_planes, ctx->src_stride, 0, ctx->h, dst_planes, ctx->dst_stride);
152 if (res != ctx->oh) {
153 GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[FFSWS] Error during scale, expected height %d got %d\n", ctx->oh, res));
154 gf_filter_pid_drop_packet(ctx->ipid);
155 gf_filter_pck_discard(dst_pck);
156 return GF_NOT_SUPPORTED;
157 }
158
159 if (ctx->swap_idx_1 || ctx->swap_idx_2) {
160 u32 i, j;
161 for (i=0; i<ctx->h; i++) {
162 u8 *dst = output + ctx->dst_stride[0]*i;
163 for (j=0; j<ctx->dst_stride[0]; j+=4) {
164 u8 tmp = dst[ctx->swap_idx_1];
165 dst[ctx->swap_idx_1] = dst[ctx->swap_idx_2];
166 dst[ctx->swap_idx_2] = tmp;
167 dst += 4;
168 }
169 }
170 }
171
172 gf_filter_pck_send(dst_pck);
173 gf_filter_pid_drop_packet(ctx->ipid);
174 return GF_OK;
175 }
176
get_sws_mode(u32 mode,u32 * has_param)177 static u32 get_sws_mode(u32 mode, u32 *has_param)
178 {
179 switch (mode) {
180 case 0: return SWS_FAST_BILINEAR;
181 case 1: return SWS_BILINEAR;
182 case 2: *has_param = 2; return SWS_BICUBIC;
183 case 3: return SWS_X;
184 case 4: return SWS_POINT;
185 case 5: return SWS_AREA;
186 case 6: return SWS_BICUBLIN;
187 case 7: *has_param = 1; return SWS_GAUSS;
188 case 8: return SWS_SINC;
189 case 9: *has_param = 1; return SWS_LANCZOS;
190 case 10: return SWS_SPLINE;
191 default: break;
192 }
193 *has_param = 2;
194 return SWS_BICUBIC;
195 }
196
ffsws_configure_pid(GF_Filter * filter,GF_FilterPid * pid,Bool is_remove)197 static GF_Err ffsws_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
198 {
199 const GF_PropertyValue *p;
200 u32 w, h, stride, ofmt;
201 GF_Fraction sar;
202 Double par[2], *par_p=NULL;
203 GF_FFSWScaleCtx *ctx = gf_filter_get_udta(filter);
204
205 if (is_remove) {
206 if (ctx->opid) {
207 gf_filter_pid_remove(ctx->opid);
208 }
209 return GF_OK;
210 }
211 if (! gf_filter_pid_check_caps(pid))
212 return GF_NOT_SUPPORTED;
213
214 if (!ctx->opid) {
215 ctx->opid = gf_filter_pid_new(filter);
216 }
217
218 if (!ctx->ipid) {
219 ctx->ipid = pid;
220 }
221
222 //if nothing is set we, consider we run as an adaptation filter, wait for caps to be set to declare output
223 if (!ctx->ofmt && !ctx->osize.x && !ctx->osize.y)
224 return GF_OK;
225
226
227
228 w = h = ofmt = stride = 0;
229 p = gf_filter_pid_get_property(pid, GF_PROP_PID_WIDTH);
230 if (p) w = p->value.uint;
231 p = gf_filter_pid_get_property(pid, GF_PROP_PID_HEIGHT);
232 if (p) h = p->value.uint;
233 p = gf_filter_pid_get_property(pid, GF_PROP_PID_STRIDE);
234 if (p) stride = p->value.uint;
235 p = gf_filter_pid_get_property(pid, GF_PROP_PID_PIXFMT);
236 if (p) ofmt = p->value.uint;
237 p = gf_filter_pid_get_property(pid, GF_PROP_PID_SAR);
238 if (p) sar = p->value.frac;
239 else sar.den = sar.num = 1;
240
241 //ctx->ofmt may be 0 if the filter is instantiated dynamically, we haven't yet been called for reconfigure
242 if (!w || !h || !ofmt) {
243 return GF_OK;
244 }
245 //copy properties at init or reconfig
246 gf_filter_pid_copy_properties(ctx->opid, ctx->ipid);
247
248 if (!ctx->ofmt)
249 ctx->ofmt = ofmt;
250
251 ctx->passthrough = GF_FALSE;
252
253 ctx->ow = ctx->osize.x ? ctx->osize.x : w;
254 ctx->oh = ctx->osize.y ? ctx->osize.y : h;
255 if ((ctx->w == w) && (ctx->h == h) && (ctx->s_pfmt == ofmt) && (ctx->stride == stride)) {
256 //nothing to reconfigure
257 }
258 //passthrough mode
259 else if ((ctx->ow == w) && (ctx->oh == h) && (ctx->s_pfmt == ofmt) && (ofmt==ctx->ofmt) ) {
260 memset(ctx->dst_stride, 0, sizeof(ctx->dst_stride));
261 gf_pixel_get_size_info(ctx->ofmt, ctx->ow, ctx->oh, &ctx->out_size, &ctx->dst_stride[0], &ctx->dst_stride[1], &ctx->nb_planes, &ctx->dst_uv_height);
262 ctx->passthrough = GF_TRUE;
263 } else {
264 u32 nb_par = 0;
265 nb_par = 0;
266 Bool res;
267 u32 mode = get_sws_mode(ctx->scale, &nb_par);
268 u32 ff_src_pfmt = ffmpeg_pixfmt_from_gpac(ofmt);
269 u32 ff_dst_pfmt = ffmpeg_pixfmt_from_gpac(ctx->ofmt);
270
271 //get layout info for source
272 memset(ctx->src_stride, 0, sizeof(ctx->src_stride));
273 if (ctx->stride) ctx->src_stride[0] = ctx->stride;
274
275
276 res = gf_pixel_get_size_info(ofmt, w, h, &ctx->out_src_size, &ctx->src_stride[0], &ctx->src_stride[1], &ctx->nb_src_planes, &ctx->src_uv_height);
277 if (!res) {
278 GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[FFSWS] Failed to query source pixel format characteristics\n"));
279 return GF_NOT_SUPPORTED;
280 }
281 if (ctx->nb_src_planes==3) ctx->src_stride[2] = ctx->src_stride[1];
282 if (ctx->nb_src_planes==4) ctx->src_stride[3] = ctx->src_stride[0];
283
284 //get layout info for dest
285 memset(ctx->dst_stride, 0, sizeof(ctx->dst_stride));
286 res = gf_pixel_get_size_info(ctx->ofmt, ctx->ow, ctx->oh, &ctx->out_size, &ctx->dst_stride[0], &ctx->dst_stride[1], &ctx->nb_planes, &ctx->dst_uv_height);
287 if (!res) {
288 GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[FFSWS] Failed to query output pixel format characteristics\n"));
289 return GF_NOT_SUPPORTED;
290 }
291 if (ctx->nb_planes==3) ctx->dst_stride[2] = ctx->dst_stride[1];
292 if (ctx->nb_planes==4) ctx->dst_stride[3] = ctx->dst_stride[0];
293
294
295 if (nb_par) {
296 if ((nb_par==1) && (ctx->p1!=GF_MAX_DOUBLE) ) {
297 par[0] = ctx->p1;
298 par_p = (Double *)par;
299 } else if ((nb_par==2) && (ctx->p1!=GF_MAX_DOUBLE) && (ctx->p2!=GF_MAX_DOUBLE)) {
300 par[0] = ctx->p1;
301 par[1] = ctx->p2;
302 par_p = (Double *)par;
303 }
304 }
305 //create/get a swscale context
306 ctx->swscaler = sws_getCachedContext(ctx->swscaler, w, h, ff_src_pfmt, ctx->ow, ctx->oh, ff_dst_pfmt, mode, NULL, NULL, par_p);
307
308 if (!ctx->swscaler) {
309 #ifndef GPAC_DISABLE_LOG
310 Bool in_ok = sws_isSupportedInput(ff_src_pfmt);
311 Bool out_ok = sws_isSupportedInput(ff_dst_pfmt);
312 GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[FFSWS] Cannot allocate context for required format - input %s output %s\n", in_ok ? "OK" : "not supported" , out_ok ? "OK" : "not supported"));
313 #endif
314 return GF_NOT_SUPPORTED;
315 }
316 ctx->w = w;
317 ctx->h = h;
318 ctx->s_pfmt = ofmt;
319 GF_LOG(GF_LOG_INFO, GF_LOG_MEDIA, ("[FFSWS] Setup rescaler from %dx%d fmt %s to %dx%d fmt %s\n", w, h, gf_pixel_fmt_name(ofmt), ctx->ow, ctx->oh, gf_pixel_fmt_name(ctx->ofmt)));
320
321 ctx->swap_idx_1 = ctx->swap_idx_2 = 0;
322 //if same source / dest pixel format, don'( swap UV components
323 if (ctx->s_pfmt != ctx->ofmt) {
324 if (ctx->ofmt==GF_PIXEL_VYUY) {
325 ctx->swap_idx_1 = 0;
326 ctx->swap_idx_2 = 2;
327 }
328 else if (ctx->ofmt==GF_PIXEL_YVYU) {
329 ctx->swap_idx_1 = 1;
330 ctx->swap_idx_2 = 3;
331 }
332 }
333 }
334
335 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_WIDTH, &PROP_UINT(ctx->ow));
336 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_HEIGHT, &PROP_UINT(ctx->oh));
337 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STRIDE, &PROP_UINT(ctx->dst_stride[0]));
338 if (ctx->nb_planes>1)
339 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STRIDE_UV, &PROP_UINT(ctx->dst_stride[1]));
340
341 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CODECID, &PROP_UINT(GF_CODECID_RAW));
342 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_PIXFMT, &PROP_UINT(ctx->ofmt));
343 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SAR, &PROP_FRAC(sar) );
344 return GF_OK;
345 }
346
ffsws_finalize(GF_Filter * filter)347 static void ffsws_finalize(GF_Filter *filter)
348 {
349 GF_FFSWScaleCtx *ctx = gf_filter_get_udta(filter);
350 if (ctx->swscaler) sws_freeContext(ctx->swscaler);
351 return;
352 }
353
354
ffsws_reconfigure_output(GF_Filter * filter,GF_FilterPid * pid)355 static GF_Err ffsws_reconfigure_output(GF_Filter *filter, GF_FilterPid *pid)
356 {
357 const GF_PropertyValue *p;
358 GF_FFSWScaleCtx *ctx = gf_filter_get_udta(filter);
359 if (ctx->opid != pid) return GF_BAD_PARAM;
360
361 p = gf_filter_pid_caps_query(pid, GF_PROP_PID_WIDTH);
362 if (p) ctx->osize.x = p->value.uint;
363
364 p = gf_filter_pid_caps_query(pid, GF_PROP_PID_HEIGHT);
365 if (p) ctx->osize.y = p->value.uint;
366
367 p = gf_filter_pid_caps_query(pid, GF_PROP_PID_PIXFMT);
368 if (p) ctx->ofmt = p->value.uint;
369 return ffsws_configure_pid(filter, ctx->ipid, GF_FALSE);
370 }
371
372
373 #define OFFS(_n) #_n, offsetof(GF_FFSWScaleCtx, _n)
374 static GF_FilterArgs FFSWSArgs[] =
375 {
376 { OFFS(osize), "osize of output video. When not set, input osize is used", GF_PROP_VEC2I, NULL, NULL, 0},
377 { OFFS(ofmt), "pixel format for output video. When not set, input format is used", GF_PROP_PIXFMT, "none", NULL, 0},
378 { OFFS(scale), "scaling mode - see filter info", GF_PROP_UINT, "bicubic", "fastbilinear|bilinear|bicubic|X|point|area|bicublin|gauss|sinc|lanzcos|spline", GF_FS_ARG_HINT_ADVANCED},
379 { OFFS(p1), "scaling algo param1 - see filter info", GF_PROP_DOUBLE, "+I", NULL, GF_FS_ARG_HINT_ADVANCED},
380 { OFFS(p2), "scaling algo param2 - see filter info", GF_PROP_DOUBLE, "+I", NULL, GF_FS_ARG_HINT_ADVANCED},
381 {0}
382 };
383
384 static const GF_FilterCapability FFSWSCaps[] =
385 {
386 CAP_UINT(GF_CAPS_INPUT_OUTPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
387 CAP_UINT(GF_CAPS_INPUT_OUTPUT,GF_PROP_PID_CODECID, GF_CODECID_RAW)
388 };
389
390
391 GF_FilterRegister FFSWSRegister = {
392 .name = "ffsws",
393 .version=LIBSWSCALE_IDENT,
394 GF_FS_SET_DESCRIPTION("FFMPEG video rescaler")
395 GF_FS_SET_HELP("For bicubic, to tune the shape of the basis function, [-p1]() tunes f(1) and [-p2]() f´(1)\n"\
396 "For gauss [-p1]() tunes the exponent and thus cutoff frequency\n"\
397 "For lanczos [-p1]() tunes the width of the window function"\
398 "\nSee FFMPEG documentation (https://ffmpeg.org/documentation.html) for more details")
399
400 .private_size = sizeof(GF_FFSWScaleCtx),
401 .args = FFSWSArgs,
402 .configure_pid = ffsws_configure_pid,
403 SETCAPS(FFSWSCaps),
404 .finalize = ffsws_finalize,
405 .process = ffsws_process,
406 .reconfigure_output = ffsws_reconfigure_output,
407 };
408
409 #else
410 #include <gpac/filters.h>
411 #endif //GPAC_HAS_FFMPEG
412
ffsws_register(GF_FilterSession * session)413 const GF_FilterRegister *ffsws_register(GF_FilterSession *session)
414 {
415 #ifdef GPAC_HAS_FFMPEG
416 FFSWSArgs[1].min_max_enum = gf_pixel_fmt_all_names();
417 return &FFSWSRegister;
418 #else
419 return NULL;
420 #endif
421 }
422
423