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