1 /*****************************************************************************
2 * video filter: video filter doing chroma conversion and resizing
3 * using the ffmpeg library
4 *****************************************************************************
5 * Copyright (C) 1999-2001 the VideoLAN team
8 * Authors: 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_codec.h>
35 #include <vlc_filter.h>
38 #ifdef HAVE_LIBAVCODEC_AVCODEC_H
39 # include <libavcodec/avcodec.h>
40 #elif defined(HAVE_FFMPEG_AVCODEC_H)
41 # include <ffmpeg/avcodec.h>
48 #if !defined(HAVE_LIBSWSCALE_SWSCALE_H) && !defined(HAVE_FFMPEG_SWSCALE_H) && !defined(HAVE_LIBSWSCALE_TREE)
49 void E_(InitLibavcodec) ( vlc_object_t *p_object );
50 static int CheckInit( filter_t *p_filter );
51 static picture_t *Process( filter_t *p_filter, picture_t *p_pic );
53 /*****************************************************************************
54 * filter_sys_t : filter descriptor
55 *****************************************************************************/
60 vlc_bool_t b_resize_first;
61 vlc_bool_t b_enable_croppadd;
64 int i_src_ffmpeg_chroma;
66 int i_dst_ffmpeg_chroma;
70 ImgReSampleContext *p_rsc;
73 /*****************************************************************************
74 * OpenFilterEx: common code to OpenFilter and OpenCropPadd
75 *****************************************************************************/
76 static int OpenFilterEx( vlc_object_t *p_this, vlc_bool_t b_enable_croppadd )
78 filter_t *p_filter = (filter_t*)p_this;
80 vlc_bool_t b_convert, b_resize;
82 /* Check if we can handle that formats */
83 if( E_(GetFfmpegChroma)( p_filter->fmt_in.video.i_chroma ) < 0 ||
84 E_(GetFfmpegChroma)( p_filter->fmt_out.video.i_chroma ) < 0 )
90 ( p_filter->fmt_in.video.i_width != p_filter->fmt_out.video.i_width ) ||
91 ( p_filter->fmt_in.video.i_height != p_filter->fmt_out.video.i_height );
93 if ( b_enable_croppadd )
95 b_resize = b_resize ||
96 ( p_filter->fmt_in.video.i_visible_width != p_filter->fmt_in.video.i_width ) ||
97 ( p_filter->fmt_in.video.i_visible_height != p_filter->fmt_in.video.i_height ) ||
98 ( p_filter->fmt_in.video.i_x_offset != 0 ) ||
99 ( p_filter->fmt_in.video.i_y_offset != 0 ) ||
100 ( p_filter->fmt_out.video.i_visible_width != p_filter->fmt_out.video.i_width ) ||
101 ( p_filter->fmt_out.video.i_visible_height != p_filter->fmt_out.video.i_height ) ||
102 ( p_filter->fmt_out.video.i_x_offset != 0 ) ||
103 ( p_filter->fmt_out.video.i_y_offset != 0 );
107 ( p_filter->fmt_in.video.i_chroma != p_filter->fmt_out.video.i_chroma );
109 if( !b_resize && !b_convert )
115 /* Allocate the memory needed to store the decoder's structure */
116 if( ( p_filter->p_sys = p_sys =
117 (filter_sys_t *)malloc(sizeof(filter_sys_t)) ) == NULL )
119 msg_Err( p_filter, "out of memory" );
125 p_sys->b_enable_croppadd = b_enable_croppadd;
126 p_sys->i_src_ffmpeg_chroma =
127 E_(GetFfmpegChroma)( p_filter->fmt_in.video.i_chroma );
128 p_sys->i_dst_ffmpeg_chroma =
129 E_(GetFfmpegChroma)( p_filter->fmt_out.video.i_chroma );
130 p_filter->pf_video_filter = Process;
131 es_format_Init( &p_sys->fmt_in, 0, 0 );
132 es_format_Init( &p_sys->fmt_out, 0, 0 );
134 /* Dummy alloc, will be reallocated in CheckInit */
135 avpicture_alloc( &p_sys->tmp_pic, p_sys->i_src_ffmpeg_chroma,
136 p_filter->fmt_out.video.i_width,
137 p_filter->fmt_out.video.i_height );
139 if( CheckInit( p_filter ) != VLC_SUCCESS )
141 if( p_sys->p_rsc ) img_resample_close( p_sys->p_rsc );
142 avpicture_free( &p_sys->tmp_pic );
147 msg_Dbg( p_filter, "input: %ix%i %4.4s -> %ix%i %4.4s",
148 p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height,
149 (char *)&p_filter->fmt_in.video.i_chroma,
150 p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height,
151 (char *)&p_filter->fmt_out.video.i_chroma );
153 /* libavcodec needs to be initialized for some chroma conversions */
154 E_(InitLibavcodec)(p_this);
159 /*****************************************************************************
160 * OpenFilter: probe the filter and return score
161 *****************************************************************************/
162 int E_(OpenFilter)( vlc_object_t *p_this )
164 return OpenFilterEx( p_this, VLC_FALSE );
167 /*****************************************************************************
168 * OpenCropPadd: probe the filter and return score
169 *****************************************************************************/
170 int E_(OpenCropPadd)( vlc_object_t *p_this )
172 return OpenFilterEx( p_this, VLC_TRUE );
176 /*****************************************************************************
177 * CloseFilter: clean up the filter
178 *****************************************************************************/
179 void E_(CloseFilter)( vlc_object_t *p_this )
181 filter_t *p_filter = (filter_t*)p_this;
182 filter_sys_t *p_sys = p_filter->p_sys;
184 if( p_sys->p_rsc ) img_resample_close( p_sys->p_rsc );
185 avpicture_free( &p_sys->tmp_pic );
190 /*****************************************************************************
191 * CheckInit: Initialise filter when necessary
192 *****************************************************************************/
193 static int CheckInit( filter_t *p_filter )
195 filter_sys_t *p_sys = p_filter->p_sys;
206 b_change = ( p_filter->fmt_in.video.i_width != p_sys->fmt_in.video.i_width ) ||
207 ( p_filter->fmt_in.video.i_height != p_sys->fmt_in.video.i_height ) ||
208 ( p_filter->fmt_out.video.i_width != p_sys->fmt_out.video.i_width ) ||
209 ( p_filter->fmt_out.video.i_height != p_sys->fmt_out.video.i_height );
211 if ( p_sys->b_enable_croppadd )
213 b_change = b_change ||
214 ( p_filter->fmt_in.video.i_y_offset != p_sys->fmt_in.video.i_y_offset ) ||
215 ( p_filter->fmt_in.video.i_x_offset != p_sys->fmt_in.video.i_x_offset ) ||
216 ( p_filter->fmt_in.video.i_visible_width != p_sys->fmt_in.video.i_visible_width ) ||
217 ( p_filter->fmt_in.video.i_visible_height != p_sys->fmt_in.video.i_visible_height ) ||
218 ( p_filter->fmt_out.video.i_y_offset != p_sys->fmt_out.video.i_y_offset ) ||
219 ( p_filter->fmt_out.video.i_x_offset != p_sys->fmt_out.video.i_x_offset ) ||
220 ( p_filter->fmt_out.video.i_visible_width != p_sys->fmt_out.video.i_visible_width ) ||
221 ( p_filter->fmt_out.video.i_visible_height != p_sys->fmt_out.video.i_visible_height );
226 if( p_sys->p_rsc ) img_resample_close( p_sys->p_rsc );
230 ( p_filter->fmt_in.video.i_chroma != p_filter->fmt_out.video.i_chroma );
233 ( p_filter->fmt_in.video.i_width != p_filter->fmt_out.video.i_width ) ||
234 ( p_filter->fmt_in.video.i_height != p_filter->fmt_out.video.i_height );
236 p_sys->b_resize_first =
237 ( p_filter->fmt_in.video.i_width * p_filter->fmt_in.video.i_height ) >
238 ( p_filter->fmt_out.video.i_width * p_filter->fmt_out.video.i_height );
240 if( p_sys->b_resize &&
241 ( p_sys->i_src_ffmpeg_chroma != PIX_FMT_YUV420P ) &&
242 ( p_sys->i_src_ffmpeg_chroma != PIX_FMT_YUVJ420P ) &&
243 ( p_sys->i_dst_ffmpeg_chroma != PIX_FMT_YUV420P ) &&
244 ( p_sys->i_dst_ffmpeg_chroma != PIX_FMT_YUVJ420P ) )
246 msg_Err( p_filter, "img_resample_init only deals with I420" );
249 else if( ( p_sys->i_src_ffmpeg_chroma != PIX_FMT_YUV420P ) &&
250 ( p_sys->i_src_ffmpeg_chroma != PIX_FMT_YUVJ420P ) )
252 p_sys->b_resize_first = VLC_FALSE;
254 else if( ( p_sys->i_dst_ffmpeg_chroma != PIX_FMT_YUV420P ) &&
255 ( p_sys->i_dst_ffmpeg_chroma != PIX_FMT_YUVJ420P ) )
257 p_sys->b_resize_first = VLC_TRUE;
260 if ( p_sys->b_enable_croppadd )
262 p_sys->b_resize = p_sys->b_resize ||
263 ( p_filter->fmt_in.video.i_visible_width != p_filter->fmt_in.video.i_width ) ||
264 ( p_filter->fmt_in.video.i_visible_height != p_filter->fmt_in.video.i_height ) ||
265 ( p_filter->fmt_in.video.i_x_offset != 0 ) ||
266 ( p_filter->fmt_in.video.i_y_offset != 0 ) ||
267 ( p_filter->fmt_out.video.i_visible_width != p_filter->fmt_out.video.i_width ) ||
268 ( p_filter->fmt_out.video.i_visible_height != p_filter->fmt_out.video.i_height ) ||
269 ( p_filter->fmt_out.video.i_x_offset != 0 ) ||
270 ( p_filter->fmt_out.video.i_y_offset != 0 );
273 if( p_sys->b_resize )
275 if ( p_sys->b_enable_croppadd )
277 i_croptop = p_filter->fmt_in.video.i_y_offset;
278 i_cropbottom = p_filter->fmt_in.video.i_height
279 - p_filter->fmt_in.video.i_visible_height
280 - p_filter->fmt_in.video.i_y_offset;
281 i_cropleft = p_filter->fmt_in.video.i_x_offset;
282 i_cropright = p_filter->fmt_in.video.i_width
283 - p_filter->fmt_in.video.i_visible_width
284 - p_filter->fmt_in.video.i_x_offset;
287 i_paddtop = p_filter->fmt_out.video.i_y_offset;
288 i_paddbottom = p_filter->fmt_out.video.i_height
289 - p_filter->fmt_out.video.i_visible_height
290 - p_filter->fmt_out.video.i_y_offset;
291 i_paddleft = p_filter->fmt_out.video.i_x_offset;
292 i_paddright = p_filter->fmt_out.video.i_width
293 - p_filter->fmt_out.video.i_visible_width
294 - p_filter->fmt_out.video.i_x_offset;
297 p_sys->p_rsc = img_resample_full_init(
298 p_filter->fmt_out.video.i_width,
299 p_filter->fmt_out.video.i_height,
300 p_filter->fmt_in.video.i_width,
301 p_filter->fmt_in.video.i_height,
302 i_croptop,i_cropbottom,
303 i_cropleft,i_cropright,
304 i_paddtop,i_paddbottom,
305 i_paddleft,i_paddright );
309 msg_Err( p_filter, "img_resample_init failed" );
313 msg_Dbg( p_filter, "input: %ix%i -> %ix%i",
314 p_filter->fmt_out.video.i_width,
315 p_filter->fmt_out.video.i_height,
316 p_filter->fmt_in.video.i_width,
317 p_filter->fmt_in.video.i_height);
321 avpicture_free( &p_sys->tmp_pic );
323 if( p_sys->b_resize_first )
325 /* Resizing then conversion */
326 avpicture_alloc( &p_sys->tmp_pic, p_sys->i_src_ffmpeg_chroma,
327 p_filter->fmt_out.video.i_width,
328 p_filter->fmt_out.video.i_height );
332 /* Conversion then resizing */
333 avpicture_alloc( &p_sys->tmp_pic, p_sys->i_dst_ffmpeg_chroma,
334 p_filter->fmt_in.video.i_width,
335 p_filter->fmt_in.video.i_height );
338 p_sys->fmt_in = p_filter->fmt_in;
339 p_sys->fmt_out = p_filter->fmt_out;
345 /* fill padd code from ffmpeg */
346 static int padcolor[3] = { 16, 128, 128 };
348 /* Expects img to be yuv420 */
349 static void fill_pad_region( AVPicture* img, int height, int width,
350 int padtop, int padbottom, int padleft, int padright, int *color )
355 for ( i = 0; i < 3; i++ )
357 shift = ( i == 0 ) ? 0 : 1;
359 if ( padtop || padleft )
361 memset( img->data[i], color[i], ( ( ( img->linesize[i] * padtop ) +
362 padleft ) >> shift) );
365 if ( padleft || padright )
367 optr = img->data[i] + ( img->linesize[i] * ( padtop >> shift ) ) +
368 ( img->linesize[i] - ( padright >> shift ) );
370 for ( y = 0; y < ( ( height - ( padtop + padbottom ) ) >> shift ); y++ )
372 memset( optr, color[i], ( padleft + padright ) >> shift );
373 optr += img->linesize[i];
379 optr = img->data[i] + ( img->linesize[i] * ( ( height - padbottom ) >> shift ) );
380 memset( optr, color[i], ( ( img->linesize[i] * padbottom ) >> shift ) );
385 /* Workaround, because old libavcodec doesnt know how to padd */
386 static void img_resample_padd( ImgReSampleContext *s, AVPicture *output,
387 const AVPicture *input, int padtop, int padleft )
389 AVPicture nopadd_pic = *output;
391 /* shift out top and left padding for old ffmpeg */
392 nopadd_pic.data[0] += ( nopadd_pic.linesize[0] * padtop + padleft );
393 nopadd_pic.data[1] += ( nopadd_pic.linesize[1] * padtop + padleft ) >> 1;
394 nopadd_pic.data[2] += ( nopadd_pic.linesize[2] * padtop + padleft ) >> 1;
395 img_resample( s, &nopadd_pic, input );
399 /*****************************************************************************
400 * Do the processing here
401 *****************************************************************************/
402 static picture_t *Process( filter_t *p_filter, picture_t *p_pic )
404 filter_sys_t *p_sys = p_filter->p_sys;
405 AVPicture src_pic, dest_pic;
406 AVPicture *p_src, *p_dst;
407 picture_t *p_pic_dst;
410 /* Check if format properties changed */
411 if( CheckInit( p_filter ) != VLC_SUCCESS ) return 0;
413 /* Request output picture */
414 p_pic_dst = p_filter->pf_vout_buffer_new( p_filter );
417 msg_Warn( p_filter, "can't get output picture" );
418 p_pic->pf_release( p_pic );
422 /* Prepare the AVPictures for the conversion */
423 for( i = 0; i < p_pic->i_planes; i++ )
425 src_pic.data[i] = p_pic->p[i].p_pixels;
426 src_pic.linesize[i] = p_pic->p[i].i_pitch;
428 for( i = 0; i < p_pic_dst->i_planes; i++ )
430 dest_pic.data[i] = p_pic_dst->p[i].p_pixels;
431 dest_pic.linesize[i] = p_pic_dst->p[i].i_pitch;
435 if( p_filter->fmt_in.video.i_chroma == VLC_FOURCC('Y','V','1','2') ||
436 p_filter->fmt_in.video.i_chroma == VLC_FOURCC('Y','V','U','9') )
439 src_pic.data[1] = p_pic->p[2].p_pixels;
440 src_pic.data[2] = p_pic->p[1].p_pixels;
442 if( p_filter->fmt_out.video.i_chroma == VLC_FOURCC('Y','V','1','2') ||
443 p_filter->fmt_out.video.i_chroma == VLC_FOURCC('Y','V','U','9') )
446 dest_pic.data[1] = p_pic_dst->p[2].p_pixels;
447 dest_pic.data[2] = p_pic_dst->p[1].p_pixels;
449 if( p_sys->i_src_ffmpeg_chroma == PIX_FMT_RGB24 )
450 if( p_filter->fmt_in.video.i_bmask == 0x00ff0000 )
451 p_sys->i_src_ffmpeg_chroma = PIX_FMT_BGR24;
455 if( p_sys->b_resize && p_sys->p_rsc )
458 if( p_sys->b_resize_first )
460 if( p_sys->b_convert ) p_dst = &p_sys->tmp_pic;
462 img_resample( p_sys->p_rsc, p_dst, p_src );
464 if (p_sys->b_enable_croppadd)
466 if (p_filter->fmt_out.video.i_visible_width != p_filter->fmt_out.video.i_width ||
467 p_filter->fmt_out.video.i_visible_height != p_filter->fmt_out.video.i_height ||
468 p_filter->fmt_out.video.i_x_offset != 0 ||
469 p_filter->fmt_out.video.i_y_offset != 0)
471 fill_pad_region(p_dst, p_filter->fmt_out.video.i_height,
472 p_filter->fmt_out.video.i_width,
473 p_filter->fmt_out.video.i_y_offset,
474 p_filter->fmt_out.video.i_height
475 - p_filter->fmt_out.video.i_visible_height
476 - p_filter->fmt_out.video.i_y_offset,
477 p_filter->fmt_out.video.i_x_offset,
478 p_filter->fmt_out.video.i_width
479 - p_filter->fmt_out.video.i_visible_width
480 - p_filter->fmt_out.video.i_x_offset,
489 if( p_sys->b_convert )
491 video_format_t *p_fmt = &p_filter->fmt_out.video;
493 if( p_sys->b_resize && !p_sys->b_resize_first )
495 p_dst = &p_sys->tmp_pic;
496 p_fmt = &p_filter->fmt_in.video;
499 img_convert( p_dst, p_sys->i_dst_ffmpeg_chroma,
500 p_src, p_sys->i_src_ffmpeg_chroma,
501 p_fmt->i_width, p_fmt->i_height );
506 if( p_sys->b_resize && !p_sys->b_resize_first && p_sys->p_rsc )
510 img_resample( p_sys->p_rsc, p_dst, p_src );
512 if (p_sys->b_enable_croppadd)
514 if (p_filter->fmt_out.video.i_visible_width != p_filter->fmt_out.video.i_width ||
515 p_filter->fmt_out.video.i_visible_height != p_filter->fmt_out.video.i_height ||
516 p_filter->fmt_out.video.i_x_offset != 0 ||
517 p_filter->fmt_out.video.i_y_offset != 0)
519 fill_pad_region(p_dst, p_filter->fmt_out.video.i_height,
520 p_filter->fmt_out.video.i_width,
521 p_filter->fmt_out.video.i_y_offset,
522 p_filter->fmt_out.video.i_height
523 - p_filter->fmt_out.video.i_visible_height
524 - p_filter->fmt_out.video.i_y_offset,
525 p_filter->fmt_out.video.i_x_offset,
526 p_filter->fmt_out.video.i_width
527 - p_filter->fmt_out.video.i_visible_width
528 - p_filter->fmt_out.video.i_x_offset,
534 /* Special case for RV32 -> YUVA */
535 if( !p_sys->b_resize &&
536 p_filter->fmt_in.video.i_chroma == VLC_FOURCC('R','V','3','2') &&
537 p_filter->fmt_out.video.i_chroma == VLC_FOURCC('Y','U','V','A') )
539 uint8_t *p_src = p_pic->p[0].p_pixels;
540 int i_src_pitch = p_pic->p[0].i_pitch;
541 uint8_t *p_dst = p_pic_dst->p[3].p_pixels;
542 int i_dst_pitch = p_pic_dst->p[3].i_pitch;
545 for( l = 0; l < p_filter->fmt_out.video.i_height; l++ )
547 for( j = 0; j < p_filter->fmt_out.video.i_width; j++ )
549 p_dst[j] = p_src[j*4+3];
551 p_src += i_src_pitch;
552 p_dst += i_dst_pitch;
555 else if( p_filter->fmt_out.video.i_chroma == VLC_FOURCC('Y','U','V','A') )
557 /* Special case for YUVA */
558 memset( p_pic_dst->p[3].p_pixels, 0xFF,
559 p_pic_dst->p[3].i_pitch * p_pic_dst->p[3].i_lines );
562 p_pic_dst->date = p_pic->date;
563 p_pic_dst->b_force = p_pic->b_force;
564 p_pic_dst->i_nb_fields = p_pic->i_nb_fields;
565 p_pic_dst->b_progressive = p_pic->b_progressive;
566 p_pic_dst->b_top_field_first = p_pic->b_top_field_first;
568 p_pic->pf_release( p_pic );
571 #endif /* ( (defined(HAVE_LIBSWSCALE_SWSCALE_H) || defined(HAVE_FFMPEG_SWSCALE_H) || defined(HAVE_LIBSWSCALE_TREE)) */