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 *****************************************************************************/
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_filter.h>
37 #ifdef HAVE_LIBSWSCALE_SWSCALE_H
38 # include <libswscale/swscale.h>
39 # include <libavcodec/avcodec.h>
40 #elif defined(HAVE_FFMPEG_SWSCALE_H)
41 # include <ffmpeg/swscale.h>
42 # include <ffmpeg/avcodec.h>
45 /* Gruikkkkkkkkkk!!!!! */
46 #include "../codec/avcodec/avcodec.h"
49 #define AVPALETTE_SIZE (256 * sizeof(uint32_t))
51 /*****************************************************************************
53 *****************************************************************************/
54 static int OpenScaler( vlc_object_t
* );
55 static void CloseScaler( vlc_object_t
* );
57 #define SCALEMODE_TEXT N_("Scaling mode")
58 #define SCALEMODE_LONGTEXT N_("Scaling mode to use.")
60 static const int pi_mode_values
[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
61 const char *const ppsz_mode_descriptions
[] =
62 { N_("Fast bilinear"), N_("Bilinear"), N_("Bicubic (good quality)"),
63 N_("Experimental"), N_("Nearest neighbour (bad quality)"),
64 N_("Area"), N_("Luma bicubic / chroma bilinear"), N_("Gauss"),
65 N_("SincR"), N_("Lanczos"), N_("Bicubic spline") };
68 set_description( N_("Video scaling filter") )
69 set_shortname( N_("Swscale" ) )
70 set_capability( "video filter2", 150 )
71 set_category( CAT_VIDEO
)
72 set_subcategory( SUBCAT_VIDEO_VFILTER
)
73 set_callbacks( OpenScaler
, CloseScaler
)
74 add_integer( "swscale-mode", 2, NULL
, SCALEMODE_TEXT
, SCALEMODE_LONGTEXT
, true )
75 change_integer_list( pi_mode_values
, ppsz_mode_descriptions
, NULL
)
78 /* Version checking */
79 #if LIBSWSCALE_VERSION_INT >= ((0<<16)+(5<<8)+0)
80 /****************************************************************************
82 ****************************************************************************/
84 void *( *swscale_fast_memcpy
)( void *, const void *, size_t );
87 * Internal swscale filter structure.
91 SwsFilter
*p_src_filter
;
92 SwsFilter
*p_dst_filter
;
93 int i_cpu_mask
, i_sws_flags
;
95 video_format_t fmt_in
;
96 video_format_t fmt_out
;
98 struct SwsContext
*ctx
;
99 struct SwsContext
*ctxA
;
109 static picture_t
*Filter( filter_t
*, picture_t
* );
110 static int Init( filter_t
* );
111 static void Clean( filter_t
* );
121 } ScalerConfiguration
;
123 static int GetParameters( ScalerConfiguration
*,
124 const video_format_t
*p_fmti
,
125 const video_format_t
*p_fmto
,
126 int i_sws_flags_default
);
128 static int GetSwsCpuMask(void);
130 /* SwScaler point resize quality seems really bad, let our scale module do it
131 * (change it to true to try) */
132 #define ALLOW_YUVP (false)
133 /* SwScaler does not like too small picture */
134 #define MINIMUM_WIDTH (32)
136 /* XXX is it always 3 even for BIG_ENDIAN (blend.c seems to think so) ? */
139 /*****************************************************************************
140 * OpenScaler: probe the filter and return score
141 *****************************************************************************/
142 static int OpenScaler( vlc_object_t
*p_this
)
144 filter_t
*p_filter
= (filter_t
*)p_this
;
149 if( GetParameters( NULL
,
150 &p_filter
->fmt_in
.video
,
151 &p_filter
->fmt_out
.video
, 0 ) )
155 p_filter
->pf_video_filter
= Filter
;
156 /* Allocate the memory needed to store the decoder's structure */
157 if( ( p_filter
->p_sys
= p_sys
= malloc(sizeof(filter_sys_t
)) ) == NULL
)
160 /* FIXME pointer assignment may not be atomic */
161 swscale_fast_memcpy
= vlc_memcpy
;
163 /* Set CPU capabilities */
164 p_sys
->i_cpu_mask
= GetSwsCpuMask();
167 i_sws_mode
= var_CreateGetInteger( p_filter
, "swscale-mode" );
170 case 0: p_sys
->i_sws_flags
= SWS_FAST_BILINEAR
; break;
171 case 1: p_sys
->i_sws_flags
= SWS_BILINEAR
; break;
172 case 2: p_sys
->i_sws_flags
= SWS_BICUBIC
; break;
173 case 3: p_sys
->i_sws_flags
= SWS_X
; break;
174 case 4: p_sys
->i_sws_flags
= SWS_POINT
; break;
175 case 5: p_sys
->i_sws_flags
= SWS_AREA
; break;
176 case 6: p_sys
->i_sws_flags
= SWS_BICUBLIN
; break;
177 case 7: p_sys
->i_sws_flags
= SWS_GAUSS
; break;
178 case 8: p_sys
->i_sws_flags
= SWS_SINC
; break;
179 case 9: p_sys
->i_sws_flags
= SWS_LANCZOS
; break;
180 case 10: p_sys
->i_sws_flags
= SWS_SPLINE
; break;
181 default: p_sys
->i_sws_flags
= SWS_BICUBIC
; i_sws_mode
= 2; break;
184 p_sys
->p_src_filter
= NULL
;
185 p_sys
->p_dst_filter
= NULL
;
190 p_sys
->p_src_a
= NULL
;
191 p_sys
->p_dst_a
= NULL
;
192 p_sys
->p_src_e
= NULL
;
193 p_sys
->p_dst_e
= NULL
;
194 memset( &p_sys
->fmt_in
, 0, sizeof(p_sys
->fmt_in
) );
195 memset( &p_sys
->fmt_out
, 0, sizeof(p_sys
->fmt_out
) );
197 if( Init( p_filter
) )
199 if( p_sys
->p_src_filter
)
200 sws_freeFilter( p_sys
->p_src_filter
);
205 msg_Dbg( p_filter
, "%ix%i chroma: %4.4s -> %ix%i chroma: %4.4s with scaling using %s",
206 p_filter
->fmt_in
.video
.i_width
, p_filter
->fmt_in
.video
.i_height
,
207 (char *)&p_filter
->fmt_in
.video
.i_chroma
,
208 p_filter
->fmt_out
.video
.i_width
, p_filter
->fmt_out
.video
.i_height
,
209 (char *)&p_filter
->fmt_out
.video
.i_chroma
,
210 ppsz_mode_descriptions
[i_sws_mode
] );
215 /*****************************************************************************
216 * CloseFilter: clean up the filter
217 *****************************************************************************/
218 static void CloseScaler( vlc_object_t
*p_this
)
220 filter_t
*p_filter
= (filter_t
*)p_this
;
221 filter_sys_t
*p_sys
= p_filter
->p_sys
;
224 if( p_sys
->p_src_filter
)
225 sws_freeFilter( p_sys
->p_src_filter
);
229 /*****************************************************************************
231 *****************************************************************************/
232 static int GetSwsCpuMask(void)
234 const unsigned int i_cpu
= vlc_CPU();
237 if( i_cpu
& CPU_CAPABILITY_MMX
)
238 i_sws_cpu
|= SWS_CPU_CAPS_MMX
;
239 #if (LIBSWSCALE_VERSION_INT >= ((0<<16)+(5<<8)+0))
240 if( i_cpu
& CPU_CAPABILITY_MMXEXT
)
241 i_sws_cpu
|= SWS_CPU_CAPS_MMX2
;
243 if( i_cpu
& CPU_CAPABILITY_3DNOW
)
244 i_sws_cpu
|= SWS_CPU_CAPS_3DNOW
;
246 if( i_cpu
& CPU_CAPABILITY_ALTIVEC
)
247 i_sws_cpu
|= SWS_CPU_CAPS_ALTIVEC
;
251 static bool IsFmtSimilar( const video_format_t
*p_fmt1
, const video_format_t
*p_fmt2
)
253 return p_fmt1
->i_chroma
== p_fmt2
->i_chroma
&&
254 p_fmt1
->i_width
== p_fmt2
->i_width
&&
255 p_fmt1
->i_height
== p_fmt2
->i_height
;
258 static int GetParameters( ScalerConfiguration
*p_cfg
,
259 const video_format_t
*p_fmti
,
260 const video_format_t
*p_fmto
,
261 int i_sws_flags_default
)
266 bool b_has_ai
= false;
267 bool b_has_ao
= false;
268 int i_sws_flags
= i_sws_flags_default
;
270 GetFfmpegChroma( &i_fmti
, *p_fmti
);
271 GetFfmpegChroma( &i_fmto
, *p_fmto
);
273 if( p_fmti
->i_chroma
== p_fmto
->i_chroma
)
275 if( p_fmti
->i_chroma
== VLC_CODEC_YUVP
&& ALLOW_YUVP
)
277 i_fmti
= i_fmto
= PIX_FMT_GRAY8
;
278 i_sws_flags
= SWS_POINT
;
282 if( p_fmti
->i_chroma
== VLC_CODEC_YUVA
)
284 i_fmti
= PIX_FMT_YUV444P
;
287 else if( p_fmti
->i_chroma
== VLC_CODEC_RGBA
)
289 i_fmti
= PIX_FMT_BGR32
;
293 if( p_fmto
->i_chroma
== VLC_CODEC_YUVA
)
295 i_fmto
= PIX_FMT_YUV444P
;
298 else if( p_fmto
->i_chroma
== VLC_CODEC_RGBA
)
300 i_fmto
= PIX_FMT_BGR32
;
304 /* FIXME TODO removed when ffmpeg is fixed
305 * Without SWS_ACCURATE_RND the quality is really bad for some conversions */
311 i_sws_flags
|= SWS_ACCURATE_RND
;
317 p_cfg
->i_fmti
= i_fmti
;
318 p_cfg
->i_fmto
= i_fmto
;
319 p_cfg
->b_has_a
= b_has_ai
&& b_has_ao
;
320 p_cfg
->b_add_a
= (!b_has_ai
) && b_has_ao
;
321 p_cfg
->b_copy
= i_fmti
== i_fmto
&& p_fmti
->i_width
== p_fmto
->i_width
&& p_fmti
->i_height
== p_fmto
->i_height
;
322 p_cfg
->i_sws_flags
= i_sws_flags
;
325 if( i_fmti
< 0 || i_fmto
< 0 )
331 static int Init( filter_t
*p_filter
)
333 filter_sys_t
*p_sys
= p_filter
->p_sys
;
334 const video_format_t
*p_fmti
= &p_filter
->fmt_in
.video
;
335 const video_format_t
*p_fmto
= &p_filter
->fmt_out
.video
;
337 if( IsFmtSimilar( p_fmti
, &p_sys
->fmt_in
) &&
338 IsFmtSimilar( p_fmto
, &p_sys
->fmt_out
) &&
345 /* Init with new parameters */
346 ScalerConfiguration cfg
;
347 if( GetParameters( &cfg
, p_fmti
, p_fmto
, p_sys
->i_sws_flags
) )
349 msg_Err( p_filter
, "format not supported" );
352 if( p_fmti
->i_width
<= 0 || p_fmto
->i_width
<= 0 )
354 msg_Err( p_filter
, "0 width not supported" );
358 /* swscale does not like too small width */
359 p_sys
->i_extend_factor
= 1;
360 while( __MIN( p_fmti
->i_width
, p_fmto
->i_width
) * p_sys
->i_extend_factor
< MINIMUM_WIDTH
)
361 p_sys
->i_extend_factor
++;
363 const unsigned i_fmti_width
= p_fmti
->i_width
* p_sys
->i_extend_factor
;
364 const unsigned i_fmto_width
= p_fmto
->i_width
* p_sys
->i_extend_factor
;
365 for( int n
= 0; n
< (cfg
.b_has_a
? 2 : 1); n
++ )
367 const int i_fmti
= n
== 0 ? cfg
.i_fmti
: PIX_FMT_GRAY8
;
368 const int i_fmto
= n
== 0 ? cfg
.i_fmto
: PIX_FMT_GRAY8
;
369 struct SwsContext
*ctx
;
371 ctx
= sws_getContext( i_fmti_width
, p_fmti
->i_height
, i_fmti
,
372 i_fmto_width
, p_fmto
->i_height
, i_fmto
,
373 cfg
.i_sws_flags
| p_sys
->i_cpu_mask
,
374 p_sys
->p_src_filter
, p_sys
->p_dst_filter
, 0 );
382 p_sys
->p_src_a
= picture_New( VLC_CODEC_GREY
, i_fmti_width
, p_fmti
->i_height
, 0 );
383 p_sys
->p_dst_a
= picture_New( VLC_CODEC_GREY
, i_fmto_width
, p_fmto
->i_height
, 0 );
385 if( p_sys
->i_extend_factor
!= 1 )
387 p_sys
->p_src_e
= picture_New( p_fmti
->i_chroma
, i_fmti_width
, p_fmti
->i_height
, 0 );
388 p_sys
->p_dst_e
= picture_New( p_fmto
->i_chroma
, i_fmto_width
, p_fmto
->i_height
, 0 );
390 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
);
391 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
);
395 ( cfg
.b_has_a
&& ( !p_sys
->ctxA
|| !p_sys
->p_src_a
|| !p_sys
->p_dst_a
) ) ||
396 ( p_sys
->i_extend_factor
!= 1 && ( !p_sys
->p_src_e
|| !p_sys
->p_dst_e
) ) )
398 msg_Err( p_filter
, "could not init SwScaler and/or allocate memory" );
403 p_sys
->b_add_a
= cfg
.b_add_a
;
404 p_sys
->b_copy
= cfg
.b_copy
;
405 p_sys
->fmt_in
= *p_fmti
;
406 p_sys
->fmt_out
= *p_fmto
;
409 msg_Dbg( p_filter
, "%ix%i chroma: %4.4s -> %ix%i chroma: %4.4s extend by %d",
410 p_fmti
->i_width
, p_fmti
->i_height
, (char *)&p_fmti
->i_chroma
,
411 p_fmto
->i_width
, p_fmto
->i_height
, (char *)&p_fmto
->i_chroma
,
412 p_sys
->i_extend_factor
);
416 static void Clean( filter_t
*p_filter
)
418 filter_sys_t
*p_sys
= p_filter
->p_sys
;
421 picture_Release( p_sys
->p_src_e
);
423 picture_Release( p_sys
->p_dst_e
);
426 picture_Release( p_sys
->p_src_a
);
428 picture_Release( p_sys
->p_dst_a
);
431 sws_freeContext( p_sys
->ctxA
);
434 sws_freeContext( p_sys
->ctx
);
436 /* We have to set it to null has we call be called again :( */
439 p_sys
->p_src_a
= NULL
;
440 p_sys
->p_dst_a
= NULL
;
441 p_sys
->p_src_e
= NULL
;
442 p_sys
->p_dst_e
= NULL
;
445 static void GetPixels( uint8_t *pp_pixel
[4], int pi_pitch
[4],
446 const picture_t
*p_picture
,
447 int i_plane_start
, int i_plane_count
)
450 for( n
= 0; n
< __MIN(i_plane_count
, p_picture
->i_planes
-i_plane_start
); n
++ )
452 pp_pixel
[n
] = p_picture
->p
[i_plane_start
+n
].p_pixels
;
453 pi_pitch
[n
] = p_picture
->p
[i_plane_start
+n
].i_pitch
;
462 static void ExtractA( picture_t
*p_dst
, const picture_t
*p_src
, unsigned i_width
, unsigned i_height
)
464 plane_t
*d
= &p_dst
->p
[0];
465 const plane_t
*s
= &p_src
->p
[0];
467 for( unsigned y
= 0; y
< i_height
; y
++ )
468 for( unsigned x
= 0; x
< i_width
; x
++ )
469 d
->p_pixels
[y
*d
->i_pitch
+x
] = s
->p_pixels
[y
*s
->i_pitch
+4*x
+OFFSET_A
];
471 static void InjectA( picture_t
*p_dst
, const picture_t
*p_src
, unsigned i_width
, unsigned i_height
)
473 plane_t
*d
= &p_dst
->p
[0];
474 const plane_t
*s
= &p_src
->p
[0];
476 for( unsigned y
= 0; y
< i_height
; y
++ )
477 for( unsigned x
= 0; x
< i_width
; x
++ )
478 d
->p_pixels
[y
*d
->i_pitch
+4*x
+OFFSET_A
] = s
->p_pixels
[y
*s
->i_pitch
+x
];
480 static void FillA( plane_t
*d
, int i_offset
)
482 for( int y
= 0; y
< d
->i_visible_lines
; y
++ )
483 for( int x
= 0; x
< d
->i_visible_pitch
; x
+= d
->i_pixel_pitch
)
484 d
->p_pixels
[y
*d
->i_pitch
+x
+i_offset
] = 0xff;
487 static void CopyPad( picture_t
*p_dst
, const picture_t
*p_src
)
489 picture_Copy( p_dst
, p_src
);
490 for( int n
= 0; n
< p_dst
->i_planes
; n
++ )
492 const plane_t
*s
= &p_src
->p
[n
];
493 plane_t
*d
= &p_dst
->p
[n
];
495 for( int y
= 0; y
< s
->i_lines
; y
++ )
497 for( int x
= s
->i_visible_pitch
; x
< d
->i_visible_pitch
; x
+= s
->i_pixel_pitch
)
498 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
);
503 static void Convert( filter_t
*p_filter
, struct SwsContext
*ctx
,
504 picture_t
*p_dst
, picture_t
*p_src
, int i_height
, int i_plane_start
, int i_plane_count
)
506 uint8_t palette
[AVPALETTE_SIZE
];
508 uint8_t *src
[4]; int src_stride
[4];
509 uint8_t *dst
[4]; int dst_stride
[4];
511 GetPixels( src
, src_stride
, p_src
, i_plane_start
, i_plane_count
);
512 if( p_filter
->fmt_in
.video
.i_chroma
== VLC_CODEC_RGBP
)
514 memset( palette
, 0, sizeof(palette
) );
515 if( p_filter
->fmt_in
.video
.p_palette
)
516 memcpy( palette
, p_filter
->fmt_in
.video
.p_palette
->palette
,
517 __MIN( sizeof(video_palette_t
), AVPALETTE_SIZE
) );
522 GetPixels( dst
, dst_stride
, p_dst
, i_plane_start
, i_plane_count
);
524 #if LIBSWSCALE_VERSION_INT >= ((0<<16)+(5<<8)+0)
525 sws_scale( ctx
, src
, src_stride
, 0, i_height
,
528 sws_scale_ordered( ctx
, src
, src_stride
, 0, i_height
,
533 /****************************************************************************
534 * Filter: the whole thing
535 ****************************************************************************
536 * This function is called just after the thread is launched.
537 ****************************************************************************/
538 static picture_t
*Filter( filter_t
*p_filter
, picture_t
*p_pic
)
540 filter_sys_t
*p_sys
= p_filter
->p_sys
;
541 const video_format_t
*p_fmti
= &p_filter
->fmt_in
.video
;
542 const video_format_t
*p_fmto
= &p_filter
->fmt_out
.video
;
543 picture_t
*p_pic_dst
;
545 /* Check if format properties changed */
546 if( Init( p_filter
) )
548 picture_Release( p_pic
);
552 /* Request output picture */
553 p_pic_dst
= filter_NewPicture( p_filter
);
556 picture_Release( p_pic
);
561 picture_t
*p_src
= p_pic
;
562 picture_t
*p_dst
= p_pic_dst
;
563 if( p_sys
->i_extend_factor
!= 1 )
565 p_src
= p_sys
->p_src_e
;
566 p_dst
= p_sys
->p_dst_e
;
568 CopyPad( p_src
, p_pic
);
572 picture_CopyPixels( p_dst
, p_src
);
574 Convert( p_filter
, p_sys
->ctx
, p_dst
, p_src
, p_fmti
->i_height
, 0, 3 );
577 /* We extract the A plane to rescale it, and then we reinject it. */
578 if( p_fmti
->i_chroma
== VLC_CODEC_RGBA
)
579 ExtractA( p_sys
->p_src_a
, p_src
, p_fmti
->i_width
* p_sys
->i_extend_factor
, p_fmti
->i_height
);
581 plane_CopyPixels( p_sys
->p_src_a
->p
, p_src
->p
+A_PLANE
);
583 Convert( p_filter
, p_sys
->ctxA
, p_sys
->p_dst_a
, p_sys
->p_src_a
, p_fmti
->i_height
, 0, 1 );
584 if( p_fmto
->i_chroma
== VLC_CODEC_RGBA
)
585 InjectA( p_dst
, p_sys
->p_dst_a
, p_fmto
->i_width
* p_sys
->i_extend_factor
, p_fmto
->i_height
);
587 plane_CopyPixels( p_dst
->p
+A_PLANE
, p_sys
->p_dst_a
->p
);
589 else if( p_sys
->b_add_a
)
591 /* We inject a complete opaque alpha plane */
592 if( p_fmto
->i_chroma
== VLC_CODEC_RGBA
)
593 FillA( &p_dst
->p
[0], OFFSET_A
);
595 FillA( &p_dst
->p
[A_PLANE
], 0 );
598 if( p_sys
->i_extend_factor
!= 1 )
600 picture_CopyPixels( p_pic_dst
, p_dst
);
603 picture_CopyProperties( p_pic_dst
, p_pic
);
604 picture_Release( p_pic
);
608 #else /* LIBSWSCALE_VERSION_INT >= ((0<<16)+(5<<8)+0) */
610 int OpenScaler( vlc_object_t
*p_this
)
615 void CloseScaler( vlc_object_t
*p_this
)
619 #endif /* LIBSWSCALE_VERSION_INT >= ((0<<16)+(5<<8)+0) */