1 /*****************************************************************************
2 * swscale.c: scaling and chroma conversion using libswscale
3 *****************************************************************************
4 * Copyright (C) 1999-2008 the VideoLAN team
7 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8 * Gildas Bazin <gbazin@videolan.org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program 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 General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_filter.h>
38 #include <libswscale/swscale.h>
40 #include "../codec/avcodec/chroma.h" // Chroma Avutil <-> VLC conversion
42 /* Gruikkkkkkkkkk!!!!! */
44 #define AVPALETTE_SIZE (256 * sizeof(uint32_t))
46 /*****************************************************************************
48 *****************************************************************************/
49 static int OpenScaler( vlc_object_t
* );
50 static void CloseScaler( vlc_object_t
* );
52 #define SCALEMODE_TEXT N_("Scaling mode")
53 #define SCALEMODE_LONGTEXT N_("Scaling mode to use.")
55 static const int pi_mode_values
[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
56 const char *const ppsz_mode_descriptions
[] =
57 { N_("Fast bilinear"), N_("Bilinear"), N_("Bicubic (good quality)"),
58 N_("Experimental"), N_("Nearest neighbour (bad quality)"),
59 N_("Area"), N_("Luma bicubic / chroma bilinear"), N_("Gauss"),
60 N_("SincR"), N_("Lanczos"), N_("Bicubic spline") };
63 set_description( N_("Video scaling filter") )
64 set_shortname( N_("Swscale" ) )
65 set_capability( "video filter2", 150 )
66 set_category( CAT_VIDEO
)
67 set_subcategory( SUBCAT_VIDEO_VFILTER
)
68 set_callbacks( OpenScaler
, CloseScaler
)
69 add_integer( "swscale-mode", 2, SCALEMODE_TEXT
, SCALEMODE_LONGTEXT
, true )
70 change_integer_list( pi_mode_values
, ppsz_mode_descriptions
)
73 /* Version checking */
74 #if LIBSWSCALE_VERSION_INT >= ((0<<16)+(5<<8)+0)
75 /****************************************************************************
77 ****************************************************************************/
79 void *( *swscale_fast_memcpy
)( void *, const void *, size_t );
82 * Internal swscale filter structure.
86 SwsFilter
*p_src_filter
;
87 SwsFilter
*p_dst_filter
;
88 int i_cpu_mask
, i_sws_flags
;
90 video_format_t fmt_in
;
91 video_format_t fmt_out
;
93 struct SwsContext
*ctx
;
94 struct SwsContext
*ctxA
;
106 static picture_t
*Filter( filter_t
*, picture_t
* );
107 static int Init( filter_t
* );
108 static void Clean( filter_t
* );
120 } ScalerConfiguration
;
122 static int GetParameters( ScalerConfiguration
*,
123 const video_format_t
*p_fmti
,
124 const video_format_t
*p_fmto
,
125 int i_sws_flags_default
);
127 static int GetSwsCpuMask(void);
129 /* SwScaler point resize quality seems really bad, let our scale module do it
130 * (change it to true to try) */
131 #define ALLOW_YUVP (false)
132 /* SwScaler does not like too small picture */
133 #define MINIMUM_WIDTH (32)
135 /* XXX is it always 3 even for BIG_ENDIAN (blend.c seems to think so) ? */
138 /*****************************************************************************
139 * OpenScaler: probe the filter and return score
140 *****************************************************************************/
141 static int OpenScaler( vlc_object_t
*p_this
)
143 filter_t
*p_filter
= (filter_t
*)p_this
;
148 if( GetParameters( NULL
,
149 &p_filter
->fmt_in
.video
,
150 &p_filter
->fmt_out
.video
, 0 ) )
154 p_filter
->pf_video_filter
= Filter
;
155 /* Allocate the memory needed to store the decoder's structure */
156 if( ( p_filter
->p_sys
= p_sys
= malloc(sizeof(filter_sys_t
)) ) == NULL
)
159 /* FIXME pointer assignment may not be atomic */
160 swscale_fast_memcpy
= vlc_memcpy
;
162 /* Set CPU capabilities */
163 p_sys
->i_cpu_mask
= GetSwsCpuMask();
166 i_sws_mode
= var_CreateGetInteger( p_filter
, "swscale-mode" );
169 case 0: p_sys
->i_sws_flags
= SWS_FAST_BILINEAR
; break;
170 case 1: p_sys
->i_sws_flags
= SWS_BILINEAR
; break;
171 case 2: p_sys
->i_sws_flags
= SWS_BICUBIC
; break;
172 case 3: p_sys
->i_sws_flags
= SWS_X
; break;
173 case 4: p_sys
->i_sws_flags
= SWS_POINT
; break;
174 case 5: p_sys
->i_sws_flags
= SWS_AREA
; break;
175 case 6: p_sys
->i_sws_flags
= SWS_BICUBLIN
; break;
176 case 7: p_sys
->i_sws_flags
= SWS_GAUSS
; break;
177 case 8: p_sys
->i_sws_flags
= SWS_SINC
; break;
178 case 9: p_sys
->i_sws_flags
= SWS_LANCZOS
; break;
179 case 10: p_sys
->i_sws_flags
= SWS_SPLINE
; break;
180 default: p_sys
->i_sws_flags
= SWS_BICUBIC
; i_sws_mode
= 2; break;
183 p_sys
->p_src_filter
= NULL
;
184 p_sys
->p_dst_filter
= NULL
;
189 p_sys
->p_src_a
= NULL
;
190 p_sys
->p_dst_a
= NULL
;
191 p_sys
->p_src_e
= NULL
;
192 p_sys
->p_dst_e
= NULL
;
193 memset( &p_sys
->fmt_in
, 0, sizeof(p_sys
->fmt_in
) );
194 memset( &p_sys
->fmt_out
, 0, sizeof(p_sys
->fmt_out
) );
196 if( Init( p_filter
) )
198 if( p_sys
->p_src_filter
)
199 sws_freeFilter( p_sys
->p_src_filter
);
204 msg_Dbg( p_filter
, "%ix%i chroma: %4.4s -> %ix%i chroma: %4.4s with scaling using %s",
205 p_filter
->fmt_in
.video
.i_width
, p_filter
->fmt_in
.video
.i_height
,
206 (char *)&p_filter
->fmt_in
.video
.i_chroma
,
207 p_filter
->fmt_out
.video
.i_width
, p_filter
->fmt_out
.video
.i_height
,
208 (char *)&p_filter
->fmt_out
.video
.i_chroma
,
209 ppsz_mode_descriptions
[i_sws_mode
] );
214 /*****************************************************************************
215 * CloseFilter: clean up the filter
216 *****************************************************************************/
217 static void CloseScaler( vlc_object_t
*p_this
)
219 filter_t
*p_filter
= (filter_t
*)p_this
;
220 filter_sys_t
*p_sys
= p_filter
->p_sys
;
223 if( p_sys
->p_src_filter
)
224 sws_freeFilter( p_sys
->p_src_filter
);
228 /*****************************************************************************
230 *****************************************************************************/
231 static int GetSwsCpuMask(void)
233 const unsigned int i_cpu
= vlc_CPU();
236 if( i_cpu
& CPU_CAPABILITY_MMX
)
237 i_sws_cpu
|= SWS_CPU_CAPS_MMX
;
238 #if (LIBSWSCALE_VERSION_INT >= ((0<<16)+(5<<8)+0))
239 if( i_cpu
& CPU_CAPABILITY_MMXEXT
)
240 i_sws_cpu
|= SWS_CPU_CAPS_MMX2
;
242 if( i_cpu
& CPU_CAPABILITY_3DNOW
)
243 i_sws_cpu
|= SWS_CPU_CAPS_3DNOW
;
245 if( i_cpu
& CPU_CAPABILITY_ALTIVEC
)
246 i_sws_cpu
|= SWS_CPU_CAPS_ALTIVEC
;
250 static bool IsFmtSimilar( const video_format_t
*p_fmt1
, const video_format_t
*p_fmt2
)
252 return p_fmt1
->i_chroma
== p_fmt2
->i_chroma
&&
253 p_fmt1
->i_width
== p_fmt2
->i_width
&&
254 p_fmt1
->i_height
== p_fmt2
->i_height
;
257 static void FixParameters( int *pi_fmt
, bool *pb_has_a
, bool *pb_swap_uv
, vlc_fourcc_t fmt
)
262 *pi_fmt
= PIX_FMT_YUV444P
;
266 *pi_fmt
= PIX_FMT_BGR32
;
270 *pi_fmt
= PIX_FMT_YUV420P
;
274 *pi_fmt
= PIX_FMT_YUV410P
;
282 static int GetParameters( ScalerConfiguration
*p_cfg
,
283 const video_format_t
*p_fmti
,
284 const video_format_t
*p_fmto
,
285 int i_sws_flags_default
)
290 bool b_has_ai
= false;
291 bool b_has_ao
= false;
292 int i_sws_flags
= i_sws_flags_default
;
293 bool b_swap_uvi
= false;
294 bool b_swap_uvo
= false;
296 GetFfmpegChroma( &i_fmti
, *p_fmti
);
297 GetFfmpegChroma( &i_fmto
, *p_fmto
);
299 if( p_fmti
->i_chroma
== p_fmto
->i_chroma
)
301 if( p_fmti
->i_chroma
== VLC_CODEC_YUVP
&& ALLOW_YUVP
)
303 i_fmti
= i_fmto
= PIX_FMT_GRAY8
;
304 i_sws_flags
= SWS_POINT
;
308 FixParameters( &i_fmti
, &b_has_ai
, &b_swap_uvi
, p_fmti
->i_chroma
);
309 FixParameters( &i_fmto
, &b_has_ao
, &b_swap_uvo
, p_fmto
->i_chroma
);
311 /* FIXME TODO removed when ffmpeg is fixed
312 * Without SWS_ACCURATE_RND the quality is really bad for some conversions */
318 i_sws_flags
|= SWS_ACCURATE_RND
;
324 p_cfg
->i_fmti
= i_fmti
;
325 p_cfg
->i_fmto
= i_fmto
;
326 p_cfg
->b_has_a
= b_has_ai
&& b_has_ao
;
327 p_cfg
->b_add_a
= (!b_has_ai
) && b_has_ao
;
328 p_cfg
->b_copy
= i_fmti
== i_fmto
&&
329 p_fmti
->i_width
== p_fmto
->i_width
&&
330 p_fmti
->i_height
== p_fmto
->i_height
;
331 p_cfg
->b_swap_uvi
= b_swap_uvi
;
332 p_cfg
->b_swap_uvo
= b_swap_uvo
;
333 p_cfg
->i_sws_flags
= i_sws_flags
;
336 if( i_fmti
< 0 || i_fmto
< 0 )
342 static int Init( filter_t
*p_filter
)
344 filter_sys_t
*p_sys
= p_filter
->p_sys
;
345 const video_format_t
*p_fmti
= &p_filter
->fmt_in
.video
;
346 video_format_t
*p_fmto
= &p_filter
->fmt_out
.video
;
348 if( IsFmtSimilar( p_fmti
, &p_sys
->fmt_in
) &&
349 IsFmtSimilar( p_fmto
, &p_sys
->fmt_out
) &&
356 /* Init with new parameters */
357 ScalerConfiguration cfg
;
358 if( GetParameters( &cfg
, p_fmti
, p_fmto
, p_sys
->i_sws_flags
) )
360 msg_Err( p_filter
, "format not supported" );
363 if( p_fmti
->i_width
<= 0 || p_fmto
->i_width
<= 0 )
365 msg_Err( p_filter
, "0 width not supported" );
369 /* swscale does not like too small width */
370 p_sys
->i_extend_factor
= 1;
371 while( __MIN( p_fmti
->i_width
, p_fmto
->i_width
) * p_sys
->i_extend_factor
< MINIMUM_WIDTH
)
372 p_sys
->i_extend_factor
++;
374 const unsigned i_fmti_width
= p_fmti
->i_width
* p_sys
->i_extend_factor
;
375 const unsigned i_fmto_width
= p_fmto
->i_width
* p_sys
->i_extend_factor
;
376 for( int n
= 0; n
< (cfg
.b_has_a
? 2 : 1); n
++ )
378 const int i_fmti
= n
== 0 ? cfg
.i_fmti
: PIX_FMT_GRAY8
;
379 const int i_fmto
= n
== 0 ? cfg
.i_fmto
: PIX_FMT_GRAY8
;
380 struct SwsContext
*ctx
;
382 ctx
= sws_getContext( i_fmti_width
, p_fmti
->i_height
, i_fmti
,
383 i_fmto_width
, p_fmto
->i_height
, i_fmto
,
384 cfg
.i_sws_flags
| p_sys
->i_cpu_mask
,
385 p_sys
->p_src_filter
, p_sys
->p_dst_filter
, 0 );
393 p_sys
->p_src_a
= picture_New( VLC_CODEC_GREY
, i_fmti_width
, p_fmti
->i_height
, 0, 1 );
394 p_sys
->p_dst_a
= picture_New( VLC_CODEC_GREY
, i_fmto_width
, p_fmto
->i_height
, 0, 1 );
396 if( p_sys
->i_extend_factor
!= 1 )
398 p_sys
->p_src_e
= picture_New( p_fmti
->i_chroma
, i_fmti_width
, p_fmti
->i_height
, 0, 1 );
399 p_sys
->p_dst_e
= picture_New( p_fmto
->i_chroma
, i_fmto_width
, p_fmto
->i_height
, 0, 1 );
402 memset( p_sys
->p_src_e
->p
[0].p_pixels
, 0, p_sys
->p_src_e
->p
[0].i_pitch
* p_sys
->p_src_e
->p
[0].i_lines
);
404 memset( p_sys
->p_dst_e
->p
[0].p_pixels
, 0, p_sys
->p_dst_e
->p
[0].i_pitch
* p_sys
->p_dst_e
->p
[0].i_lines
);
408 ( cfg
.b_has_a
&& ( !p_sys
->ctxA
|| !p_sys
->p_src_a
|| !p_sys
->p_dst_a
) ) ||
409 ( p_sys
->i_extend_factor
!= 1 && ( !p_sys
->p_src_e
|| !p_sys
->p_dst_e
) ) )
411 msg_Err( p_filter
, "could not init SwScaler and/or allocate memory" );
416 p_sys
->b_add_a
= cfg
.b_add_a
;
417 p_sys
->b_copy
= cfg
.b_copy
;
418 p_sys
->fmt_in
= *p_fmti
;
419 p_sys
->fmt_out
= *p_fmto
;
420 p_sys
->b_swap_uvi
= cfg
.b_swap_uvi
;
421 p_sys
->b_swap_uvo
= cfg
.b_swap_uvo
;
423 video_format_ScaleCropAr( p_fmto
, p_fmti
);
425 msg_Dbg( p_filter
, "%ix%i chroma: %4.4s -> %ix%i chroma: %4.4s extend by %d",
426 p_fmti
->i_width
, p_fmti
->i_height
, (char *)&p_fmti
->i_chroma
,
427 p_fmto
->i_width
, p_fmto
->i_height
, (char *)&p_fmto
->i_chroma
,
428 p_sys
->i_extend_factor
);
432 static void Clean( filter_t
*p_filter
)
434 filter_sys_t
*p_sys
= p_filter
->p_sys
;
437 picture_Release( p_sys
->p_src_e
);
439 picture_Release( p_sys
->p_dst_e
);
442 picture_Release( p_sys
->p_src_a
);
444 picture_Release( p_sys
->p_dst_a
);
447 sws_freeContext( p_sys
->ctxA
);
450 sws_freeContext( p_sys
->ctx
);
452 /* We have to set it to null has we call be called again :( */
455 p_sys
->p_src_a
= NULL
;
456 p_sys
->p_dst_a
= NULL
;
457 p_sys
->p_src_e
= NULL
;
458 p_sys
->p_dst_e
= NULL
;
461 static void GetPixels( uint8_t *pp_pixel
[4], int pi_pitch
[4],
462 const picture_t
*p_picture
,
463 int i_plane_start
, int i_plane_count
,
466 assert( !b_swap_uv
|| i_plane_count
>= 3 );
468 for( n
= 0; n
< __MIN(i_plane_count
, p_picture
->i_planes
-i_plane_start
); n
++ )
470 const int nd
= ( b_swap_uv
&& n
>= 1 && n
<= 2 ) ? (3 - n
) : n
;
471 pp_pixel
[nd
] = p_picture
->p
[i_plane_start
+n
].p_pixels
;
472 pi_pitch
[nd
] = p_picture
->p
[i_plane_start
+n
].i_pitch
;
481 static void ExtractA( picture_t
*p_dst
, const picture_t
*p_src
, unsigned i_width
, unsigned i_height
)
483 plane_t
*d
= &p_dst
->p
[0];
484 const plane_t
*s
= &p_src
->p
[0];
486 for( unsigned y
= 0; y
< i_height
; y
++ )
487 for( unsigned x
= 0; x
< i_width
; x
++ )
488 d
->p_pixels
[y
*d
->i_pitch
+x
] = s
->p_pixels
[y
*s
->i_pitch
+4*x
+OFFSET_A
];
490 static void InjectA( picture_t
*p_dst
, const picture_t
*p_src
, unsigned i_width
, unsigned i_height
)
492 plane_t
*d
= &p_dst
->p
[0];
493 const plane_t
*s
= &p_src
->p
[0];
495 for( unsigned y
= 0; y
< i_height
; y
++ )
496 for( unsigned x
= 0; x
< i_width
; x
++ )
497 d
->p_pixels
[y
*d
->i_pitch
+4*x
+OFFSET_A
] = s
->p_pixels
[y
*s
->i_pitch
+x
];
499 static void FillA( plane_t
*d
, int i_offset
)
501 for( int y
= 0; y
< d
->i_visible_lines
; y
++ )
502 for( int x
= 0; x
< d
->i_visible_pitch
; x
+= d
->i_pixel_pitch
)
503 d
->p_pixels
[y
*d
->i_pitch
+x
+i_offset
] = 0xff;
506 static void CopyPad( picture_t
*p_dst
, const picture_t
*p_src
)
508 picture_Copy( p_dst
, p_src
);
509 for( int n
= 0; n
< p_dst
->i_planes
; n
++ )
511 const plane_t
*s
= &p_src
->p
[n
];
512 plane_t
*d
= &p_dst
->p
[n
];
514 for( int y
= 0; y
< s
->i_lines
; y
++ )
516 for( int x
= s
->i_visible_pitch
; x
< d
->i_visible_pitch
; x
+= s
->i_pixel_pitch
)
517 memcpy( &d
->p_pixels
[y
*d
->i_pitch
+ x
], &d
->p_pixels
[y
*d
->i_pitch
+ s
->i_visible_pitch
- s
->i_pixel_pitch
], s
->i_pixel_pitch
);
522 static void SwapUV( picture_t
*p_dst
, const picture_t
*p_src
)
524 picture_t tmp
= *p_src
;
525 tmp
.p
[1] = p_src
->p
[2];
526 tmp
.p
[2] = p_src
->p
[1];
528 picture_CopyPixels( p_dst
, &tmp
);
530 static void Convert( filter_t
*p_filter
, struct SwsContext
*ctx
,
531 picture_t
*p_dst
, picture_t
*p_src
, int i_height
, int i_plane_start
, int i_plane_count
,
532 bool b_swap_uvi
, bool b_swap_uvo
)
534 uint8_t palette
[AVPALETTE_SIZE
];
536 uint8_t *src
[4]; int src_stride
[4];
537 uint8_t *dst
[4]; int dst_stride
[4];
539 GetPixels( src
, src_stride
, p_src
, i_plane_start
, i_plane_count
, b_swap_uvi
);
540 if( p_filter
->fmt_in
.video
.i_chroma
== VLC_CODEC_RGBP
)
542 memset( palette
, 0, sizeof(palette
) );
543 if( p_filter
->fmt_in
.video
.p_palette
)
544 memcpy( palette
, p_filter
->fmt_in
.video
.p_palette
->palette
,
545 __MIN( sizeof(video_palette_t
), AVPALETTE_SIZE
) );
550 GetPixels( dst
, dst_stride
, p_dst
, i_plane_start
, i_plane_count
, b_swap_uvo
);
552 #if LIBSWSCALE_VERSION_INT >= ((0<<16)+(5<<8)+0)
553 sws_scale( ctx
, src
, src_stride
, 0, i_height
,
556 sws_scale_ordered( ctx
, src
, src_stride
, 0, i_height
,
561 /****************************************************************************
562 * Filter: the whole thing
563 ****************************************************************************
564 * This function is called just after the thread is launched.
565 ****************************************************************************/
566 static picture_t
*Filter( filter_t
*p_filter
, picture_t
*p_pic
)
568 filter_sys_t
*p_sys
= p_filter
->p_sys
;
569 const video_format_t
*p_fmti
= &p_filter
->fmt_in
.video
;
570 const video_format_t
*p_fmto
= &p_filter
->fmt_out
.video
;
571 picture_t
*p_pic_dst
;
573 /* Check if format properties changed */
574 if( Init( p_filter
) )
576 picture_Release( p_pic
);
580 /* Request output picture */
581 p_pic_dst
= filter_NewPicture( p_filter
);
584 picture_Release( p_pic
);
589 picture_t
*p_src
= p_pic
;
590 picture_t
*p_dst
= p_pic_dst
;
591 if( p_sys
->i_extend_factor
!= 1 )
593 p_src
= p_sys
->p_src_e
;
594 p_dst
= p_sys
->p_dst_e
;
596 CopyPad( p_src
, p_pic
);
599 if( p_sys
->b_copy
&& p_sys
->b_swap_uvi
== p_sys
->b_swap_uvo
)
600 picture_CopyPixels( p_dst
, p_src
);
601 else if( p_sys
->b_copy
)
602 SwapUV( p_dst
, p_src
);
604 Convert( p_filter
, p_sys
->ctx
, p_dst
, p_src
, p_fmti
->i_height
, 0, 3,
605 p_sys
->b_swap_uvi
, p_sys
->b_swap_uvo
);
608 /* We extract the A plane to rescale it, and then we reinject it. */
609 if( p_fmti
->i_chroma
== VLC_CODEC_RGBA
)
610 ExtractA( p_sys
->p_src_a
, p_src
, p_fmti
->i_width
* p_sys
->i_extend_factor
, p_fmti
->i_height
);
612 plane_CopyPixels( p_sys
->p_src_a
->p
, p_src
->p
+A_PLANE
);
614 Convert( p_filter
, p_sys
->ctxA
, p_sys
->p_dst_a
, p_sys
->p_src_a
, p_fmti
->i_height
, 0, 1, false, false );
615 if( p_fmto
->i_chroma
== VLC_CODEC_RGBA
)
616 InjectA( p_dst
, p_sys
->p_dst_a
, p_fmto
->i_width
* p_sys
->i_extend_factor
, p_fmto
->i_height
);
618 plane_CopyPixels( p_dst
->p
+A_PLANE
, p_sys
->p_dst_a
->p
);
620 else if( p_sys
->b_add_a
)
622 /* We inject a complete opaque alpha plane */
623 if( p_fmto
->i_chroma
== VLC_CODEC_RGBA
)
624 FillA( &p_dst
->p
[0], OFFSET_A
);
626 FillA( &p_dst
->p
[A_PLANE
], 0 );
629 if( p_sys
->i_extend_factor
!= 1 )
631 picture_CopyPixels( p_pic_dst
, p_dst
);
634 picture_CopyProperties( p_pic_dst
, p_pic
);
635 picture_Release( p_pic
);
639 #else /* LIBSWSCALE_VERSION_INT >= ((0<<16)+(5<<8)+0) */
641 int OpenScaler( vlc_object_t
*p_this
)
646 void CloseScaler( vlc_object_t
*p_this
)
650 #endif /* LIBSWSCALE_VERSION_INT >= ((0<<16)+(5<<8)+0) */