1 /*****************************************************************
2 * gavl - a general purpose audio/video processing library
3 *
4 * Copyright (c) 2001 - 2011 Members of the Gmerlin project
5 * gmerlin-general@lists.sourceforge.net
6 * http://gmerlin.sourceforge.net
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 * *****************************************************************/
21
22 #include <stdlib.h> /* calloc, free */
23
24 #include <string.h>
25
26 //#define DEBUG
27
28 //#ifdef DEBUG
29 #include <stdio.h>
30 //#endif
31
32 #include "gavl.h"
33 #include "config.h"
34 #include "video.h"
35
36 /***************************************************
37 * Create and destroy video converters
38 ***************************************************/
39
gavl_video_converter_create()40 gavl_video_converter_t * gavl_video_converter_create()
41 {
42 gavl_video_converter_t * ret = calloc(1,sizeof(gavl_video_converter_t));
43 gavl_video_options_set_defaults(&ret->options);
44 return ret;
45 }
46
video_converter_cleanup(gavl_video_converter_t * cnv)47 static void video_converter_cleanup(gavl_video_converter_t* cnv)
48 {
49 gavl_video_convert_context_t * ctx;
50 while(cnv->first_context)
51 {
52 ctx = cnv->first_context->next;
53
54 if(cnv->first_context->scaler)
55 gavl_video_scaler_destroy(cnv->first_context->scaler);
56 if(cnv->first_context->output_frame && cnv->first_context->next)
57 gavl_video_frame_destroy(cnv->first_context->output_frame);
58 free(cnv->first_context);
59 cnv->first_context = ctx;
60 }
61 cnv->last_context = NULL;
62 cnv->num_contexts = 0;
63 }
64
gavl_video_converter_destroy(gavl_video_converter_t * cnv)65 void gavl_video_converter_destroy(gavl_video_converter_t* cnv)
66 {
67 video_converter_cleanup(cnv);
68 free(cnv);
69 }
70
71 /* Add a context to the converter */
72
73 static gavl_video_convert_context_t *
add_context(gavl_video_converter_t * cnv,const gavl_video_format_t * input_format,const gavl_video_format_t * output_format)74 add_context(gavl_video_converter_t * cnv,
75 const gavl_video_format_t * input_format,
76 const gavl_video_format_t * output_format)
77 {
78 gavl_video_convert_context_t * ctx;
79 ctx = calloc(1, sizeof(*ctx));
80 ctx->options = &cnv->options;
81 gavl_video_format_copy(&ctx->input_format,
82 input_format);
83
84 gavl_video_format_copy(&ctx->output_format,
85 output_format);
86
87 if(cnv->last_context)
88 {
89 cnv->last_context->next = ctx;
90 cnv->last_context = cnv->last_context->next;
91 }
92 else
93 {
94 cnv->first_context = ctx;
95 cnv->last_context = ctx;
96 }
97 cnv->num_contexts++;
98 return ctx;
99 }
100
add_context_csp(gavl_video_converter_t * cnv,const gavl_video_format_t * input_format,const gavl_video_format_t * output_format)101 static int add_context_csp(gavl_video_converter_t * cnv,
102 const gavl_video_format_t * input_format,
103 const gavl_video_format_t * output_format)
104 {
105 gavl_video_convert_context_t * ctx;
106 ctx = add_context(cnv, input_format, output_format);
107
108 ctx->func = gavl_find_pixelformat_converter(&cnv->options,
109 input_format->pixelformat,
110 output_format->pixelformat,
111 input_format->frame_width,
112 input_format->frame_height);
113
114 if(!ctx->func)
115 {
116 #if 0
117 fprintf(stderr, "Found no conversion from %s to %s\n",
118 gavl_pixelformat_to_string(input_format->pixelformat),
119 gavl_pixelformat_to_string(output_format->pixelformat));
120 #endif
121 return 0;
122 }
123 #if 0
124 fprintf(stderr, "Doing pixelformat conversion from %s to %s\n",
125 gavl_pixelformat_to_string(input_format->pixelformat),
126 gavl_pixelformat_to_string(output_format->pixelformat));
127
128 #endif
129 return 1;
130 }
131
scale_func(gavl_video_convert_context_t * ctx)132 static void scale_func(gavl_video_convert_context_t * ctx)
133 {
134 gavl_video_scaler_scale(ctx->scaler,
135 ctx->input_frame,
136 ctx->output_frame);
137 }
138
add_context_scale(gavl_video_converter_t * cnv,const gavl_video_format_t * input_format,const gavl_video_format_t * output_format)139 static int add_context_scale(gavl_video_converter_t * cnv,
140 const gavl_video_format_t * input_format,
141 const gavl_video_format_t * output_format)
142 {
143 gavl_video_options_t * scaler_options;
144
145 gavl_video_convert_context_t * ctx;
146 ctx = add_context(cnv, input_format, output_format);
147
148 ctx->scaler = gavl_video_scaler_create();
149
150 scaler_options = gavl_video_scaler_get_options(ctx->scaler);
151
152 gavl_video_options_copy(scaler_options, &cnv->options);
153 #if 0
154 fprintf(stderr, "gavl_video_scaler_init:\n");
155 fprintf(stderr, "src_format:\n");
156 gavl_video_format_dump(input_format);
157 fprintf(stderr, "dst_format:\n");
158 gavl_video_format_dump(output_format);
159
160 fprintf(stderr, "src_rectangle: ");
161 gavl_rectangle_f_dump(&cnv->options.src_rect);
162 fprintf(stderr, "\n");
163
164 fprintf(stderr, "dst_rectangle: ");
165 gavl_rectangle_i_dump(&cnv->options.dst_rect);
166 fprintf(stderr, "\n");
167 #endif
168
169 if(!gavl_video_scaler_init(ctx->scaler,
170 input_format,
171 output_format))
172 {
173 // fprintf(stderr, "Initializing scaler failed\n");
174 return 0;
175 }
176 ctx->func = scale_func;
177 return 1;
178 }
179
deinterlace_func(gavl_video_convert_context_t * ctx)180 static void deinterlace_func(gavl_video_convert_context_t * ctx)
181 {
182 gavl_video_deinterlacer_deinterlace(ctx->deinterlacer,
183 ctx->input_frame,
184 ctx->output_frame);
185 }
186
add_context_deinterlace(gavl_video_converter_t * cnv,const gavl_video_format_t * in_format,const gavl_video_format_t * out_format)187 static int add_context_deinterlace(gavl_video_converter_t * cnv,
188 const gavl_video_format_t * in_format,
189 const gavl_video_format_t * out_format)
190 {
191 gavl_video_options_t * deinterlacer_options;
192 gavl_video_convert_context_t * ctx;
193 ctx = add_context(cnv, in_format, out_format);
194
195 ctx->deinterlacer = gavl_video_deinterlacer_create();
196 deinterlacer_options = gavl_video_deinterlacer_get_options(ctx->deinterlacer);
197 gavl_video_options_copy(deinterlacer_options, &cnv->options);
198
199 if(!gavl_video_deinterlacer_init(ctx->deinterlacer,
200 in_format))
201 return 0;
202
203 ctx->func = deinterlace_func;
204 return 1;
205 }
206
207
gavl_video_converter_reinit(gavl_video_converter_t * cnv)208 int gavl_video_converter_reinit(gavl_video_converter_t * cnv)
209 {
210 int csp_then_scale = 0;
211 gavl_video_convert_context_t * tmp_ctx;
212 gavl_pixelformat_t tmp_csp = GAVL_PIXELFORMAT_NONE;
213
214 int do_csp = 0;
215 int do_scale = 0;
216 int do_deinterlace = 0;
217
218 int in_sub;
219 int out_sub;
220
221 int sub_h;
222 int sub_v;
223
224 gavl_video_format_t tmp_format;
225 gavl_video_format_t tmp_format1;
226
227 gavl_video_format_t * input_format;
228 gavl_video_format_t * output_format;
229
230 input_format = &cnv->input_format;
231 output_format = &cnv->output_format;
232
233 // #ifdef DEBUG
234 #if 0
235 // fprintf(stderr, "Initializing video converter, quality: %d, Flags: 0x%08x\n",
236 // cnv->options.quality, cnv->options.accel_flags);
237 gavl_video_format_dump(input_format);
238 gavl_video_format_dump(output_format);
239
240 #endif
241
242 video_converter_cleanup(cnv);
243
244 gavl_video_format_copy(&tmp_format, input_format);
245
246 /* Adjust pixelformat */
247
248 if((cnv->options.alpha_mode == GAVL_ALPHA_IGNORE) &&
249 (tmp_format.pixelformat == GAVL_RGBA_32) &&
250 (output_format->pixelformat == GAVL_RGB_32))
251 tmp_format.pixelformat = GAVL_RGB_32;
252
253 /* Check for pixelformat conversion */
254
255 if(tmp_format.pixelformat != output_format->pixelformat)
256 {
257 do_csp = 1;
258 }
259
260 if(cnv->options.src_rect.x || cnv->options.src_rect.y ||
261 cnv->options.dst_rect.x || cnv->options.dst_rect.y ||
262 (cnv->options.src_rect.w &&
263 (cnv->options.src_rect.w != tmp_format.image_width)) ||
264 (cnv->options.src_rect.h &&
265 (cnv->options.src_rect.h != tmp_format.image_height)) ||
266 (cnv->options.dst_rect.w &&
267 (cnv->options.dst_rect.w != output_format->image_width)) ||
268 (cnv->options.dst_rect.h &&
269 (cnv->options.dst_rect.h != output_format->image_height)) ||
270 (tmp_format.image_width != output_format->image_width) ||
271 (tmp_format.image_height != output_format->image_height) ||
272 (tmp_format.pixel_width != output_format->pixel_width) ||
273 (tmp_format.pixel_height != output_format->pixel_height))
274 {
275 do_scale = 1;
276 }
277
278 /* For quality levels above 3, we switch on scaling, if it provides a more
279 accurate conversion. This is especially true if the chroma subsampling
280 ratios change or when the chroma placement becomes different */
281
282 if(((cnv->options.quality > 3) ||
283 (cnv->options.conversion_flags & GAVL_RESAMPLE_CHROMA) ||
284 do_scale))
285 {
286 if(do_csp)
287 {
288 /* Check, if pixelformat conversion can be replaced by simple scaling
289 (True if only the subsampling changes) */
290 if(gavl_pixelformat_can_scale(tmp_format.pixelformat, output_format->pixelformat))
291 {
292 do_scale = 1;
293 do_csp = 0;
294 }
295 else
296 {
297 tmp_csp = gavl_pixelformat_get_intermediate(tmp_format.pixelformat,
298 output_format->pixelformat);
299 if(tmp_csp != GAVL_PIXELFORMAT_NONE)
300 do_scale = 1;
301 }
302 }
303 /* Having different chroma placements also switches on scaling */
304 else if(tmp_format.chroma_placement != output_format->chroma_placement)
305 {
306 do_scale = 1;
307 }
308 }
309
310 /* Check if we must deinterlace */
311
312 if(((input_format->interlace_mode != GAVL_INTERLACE_NONE) &&
313 (output_format->interlace_mode == GAVL_INTERLACE_NONE)) ||
314 (cnv->options.conversion_flags & GAVL_FORCE_DEINTERLACE))
315 {
316 // fprintf(stderr, "Forcing deinterlacing\n");
317 if(cnv->options.deinterlace_mode == GAVL_DEINTERLACE_SCALE)
318 do_scale = 1;
319 else if(cnv->options.deinterlace_mode != GAVL_DEINTERLACE_NONE)
320 do_deinterlace = 1;
321 }
322
323 /* Deinterlacing must always be the first step */
324
325 if(do_deinterlace)
326 {
327 gavl_video_format_copy(&tmp_format1, &tmp_format);
328
329 tmp_format1.interlace_mode = GAVL_INTERLACE_NONE;
330 if(!add_context_deinterlace(cnv, &tmp_format, &tmp_format1))
331 return -1;
332 gavl_video_format_copy(&tmp_format, &tmp_format1);
333 }
334
335 if(do_csp && do_scale)
336 {
337 /* For qualities below 3, we scale in the pixelformat with the
338 smaller subsampling */
339
340 if(tmp_csp == GAVL_PIXELFORMAT_NONE)
341 {
342 gavl_pixelformat_chroma_sub(tmp_format.pixelformat, &sub_h, &sub_v);
343 in_sub = sub_h * sub_v;
344
345 gavl_pixelformat_chroma_sub(output_format->pixelformat, &sub_h, &sub_v);
346 out_sub = sub_h * sub_v;
347
348 if(((in_sub < out_sub) && cnv->options.quality < 3) ||
349 ((in_sub >= out_sub) && cnv->options.quality >= 3))
350 csp_then_scale = 1;
351 }
352 else
353 {
354 if(!gavl_pixelformat_can_scale(input_format->pixelformat, tmp_csp))
355 csp_then_scale = 1;
356 #if 0
357 fprintf(stderr, "converting %s -> %s -> %s (%d, %d)\n",
358 gavl_pixelformat_to_string(input_format->pixelformat),
359 gavl_pixelformat_to_string(tmp_csp),
360 gavl_pixelformat_to_string(output_format->pixelformat),
361 gavl_pixelformat_can_scale(input_format->pixelformat, tmp_csp),
362 gavl_pixelformat_can_scale(tmp_csp, output_format->pixelformat));
363 #endif
364 }
365
366 if(csp_then_scale) /* csp then scale */
367 {
368 #if 0
369 fprintf(stderr, "csp then scale\n");
370 #endif
371 /* csp (tmp_format -> tmp_format1) */
372
373 gavl_video_format_copy(&tmp_format1, &tmp_format);
374
375 if(tmp_csp != GAVL_PIXELFORMAT_NONE)
376 tmp_format1.pixelformat = tmp_csp;
377 else
378 tmp_format1.pixelformat = output_format->pixelformat;
379
380 if(!add_context_csp(cnv, &tmp_format, &tmp_format1))
381 return -1;
382
383 gavl_video_format_copy(&tmp_format, &tmp_format1);
384
385 /* scale (tmp_format -> tmp_format1) */
386
387 tmp_format1.pixelformat = output_format->pixelformat;
388
389 tmp_format1.image_width = output_format->image_width;
390 tmp_format1.image_height = output_format->image_height;
391
392 tmp_format1.pixel_width = output_format->pixel_width;
393 tmp_format1.pixel_height = output_format->pixel_height;
394
395 tmp_format1.frame_width = output_format->image_width;
396 tmp_format1.frame_height = output_format->image_height;
397 tmp_format1.chroma_placement = output_format->chroma_placement;
398 tmp_format1.interlace_mode = output_format->interlace_mode;
399
400 if(!add_context_scale(cnv, &tmp_format, &tmp_format1))
401 return -1;
402
403 gavl_video_format_copy(&tmp_format, &tmp_format1);
404
405 }
406 /* scale then csp */
407 else
408 {
409 #if 0
410 fprintf(stderr, "scale then csp\n");
411 #endif
412 /* scale (tmp_format -> tmp_format1) */
413
414 gavl_video_format_copy(&tmp_format1, &tmp_format);
415
416 tmp_format1.image_width = output_format->image_width;
417 tmp_format1.image_height = output_format->image_height;
418
419 tmp_format1.pixel_width = output_format->pixel_width;
420 tmp_format1.pixel_height = output_format->pixel_height;
421
422 tmp_format1.frame_width = output_format->image_width;
423 tmp_format1.frame_height = output_format->image_height;
424 tmp_format1.interlace_mode = output_format->interlace_mode;
425
426 if(tmp_csp != GAVL_PIXELFORMAT_NONE)
427 {
428 tmp_format1.pixelformat = tmp_csp;
429 }
430 tmp_format1.chroma_placement = output_format->chroma_placement;
431
432 if(!add_context_scale(cnv, &tmp_format, &tmp_format1))
433 return -1;
434
435 gavl_video_format_copy(&tmp_format, &tmp_format1);
436
437 /* csp (tmp_format -> tmp_format1) */
438
439 tmp_format1.pixelformat = output_format->pixelformat;
440 if(!add_context_csp(cnv, &tmp_format, &tmp_format1))
441 return -1;
442
443 gavl_video_format_copy(&tmp_format, &tmp_format1);
444 }
445
446 }
447
448 else if(do_csp)
449 {
450 if(!add_context_csp(cnv, &tmp_format,
451 output_format))
452 return -1;
453 }
454
455 else if(do_scale)
456 {
457 if(!add_context_scale(cnv, &tmp_format,
458 output_format))
459 return -1;
460 }
461
462 /* Now, create temporary frames for the contexts */
463
464 tmp_ctx = cnv->first_context;
465 while(tmp_ctx && tmp_ctx->next)
466 {
467 tmp_ctx->output_frame =
468 gavl_video_frame_create(&tmp_ctx->output_format);
469 gavl_video_frame_clear(tmp_ctx->output_frame, &tmp_ctx->output_format);
470
471
472 tmp_ctx->next->input_frame = tmp_ctx->output_frame;
473 tmp_ctx = tmp_ctx->next;
474 }
475 return cnv->num_contexts;
476 }
477
gavl_video_converter_init(gavl_video_converter_t * cnv,const gavl_video_format_t * input_format,const gavl_video_format_t * output_format)478 int gavl_video_converter_init(gavl_video_converter_t * cnv,
479 const gavl_video_format_t * input_format,
480 const gavl_video_format_t * output_format)
481 {
482 gavl_video_format_copy(&cnv->input_format, input_format);
483 gavl_video_format_copy(&cnv->output_format, output_format);
484 return gavl_video_converter_reinit(cnv);
485 }
486
487
488 gavl_video_options_t *
gavl_video_converter_get_options(gavl_video_converter_t * cnv)489 gavl_video_converter_get_options(gavl_video_converter_t * cnv)
490 {
491 return &cnv->options;
492 }
493
494 /***************************************************
495 * Convert a frame
496 ***************************************************/
497
gavl_video_convert(gavl_video_converter_t * cnv,const gavl_video_frame_t * input_frame,gavl_video_frame_t * output_frame)498 void gavl_video_convert(gavl_video_converter_t * cnv,
499 const gavl_video_frame_t * input_frame,
500 gavl_video_frame_t * output_frame)
501 {
502 gavl_video_convert_context_t * tmp_ctx;
503
504 cnv->first_context->input_frame = input_frame;
505 cnv->last_context->output_frame = output_frame;
506
507 output_frame->timestamp = input_frame->timestamp;
508 output_frame->duration = input_frame->duration;
509 output_frame->interlace_mode = input_frame->interlace_mode;
510 output_frame->timecode = input_frame->timecode;
511
512 tmp_ctx = cnv->first_context;
513
514
515 while(tmp_ctx)
516 {
517 tmp_ctx->func(tmp_ctx);
518 tmp_ctx = tmp_ctx->next;
519 }
520
521 }
522